A. Cut it Out!

枚举第一刀,那么之后每切一刀都会将原问题划分成两个子问题。

考虑DP,设$f[l][r]$表示$l$点顺时针一直到$r$点还未切割的最小代价,预处理出每条边的代价转移即可。

时间复杂度$O(n^3)$。

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=422;
const double eps=1e-8;
const double inf=1e100;
inline int sgn(double x){
if(x>eps)return 1;
if(x<-eps)return -1;
return 0;
}
inline void up(double&a,double b){if(a>b)a=b;}
int n,m,i,j,k;
double ans=inf,f[N][N];
bool v[N][N];
struct P{
double x,y;
P(){}
P(double _x,double _y){x=_x,y=_y;}
P operator-(const P&b)const{return P(x-b.x,y-b.y);}
P operator+(const P&b)const{return P(x+b.x,y+b.y);}
P operator*(const double&b)const{return P(x*b,y*b);}
double len(){return hypot(x,y);}
double len2(){return x*x+y*y;}
void read(){scanf("%lf%lf",&x,&y);}
}a[N],b[N];
double wl[N],wr[N];
inline double cross(const P&a,const P&b){return a.x*b.y-a.y*b.x;}
inline double line_intersection(const P&a,const P&b,const P&p,const P&q){
double U=cross(p-a,q-p),D=cross(b-a,q-p);
return U/D;
//return a+(b-a)*(U/D);
}
inline void pre(double&A,double&B,int k){
A=-inf,B=inf;
for(int i=0;i<n;i++){
double now=line_intersection(b[k],b[k+1],a[i],a[i+1]);
if(now<0.5&&now>A)A=now;
if(now>0.5&&now<B)B=now;
}
}
inline double cal(int k,int L,int R){
k%=m;
k+=m;
k%=m;
if(L>-100){
L%=m;
L+=m;
L%=m;
}
if(R>-100){
R%=m;
R+=m;
R%=m;
}
double A=wl[k],B=wr[k];
if(L>=0){
double now=line_intersection(b[k],b[k+1],b[L],b[L+1]);
//printf("L=%d %.10f\n",L,now);
if(now<0.5&&now>A)A=now;
if(now>0.5&&now<B)B=now;
}
if(R>=0){
double now=line_intersection(b[k],b[k+1],b[R],b[R+1]);
//printf("R=%d %.10f\n",R,now);
if(now<0.5&&now>A)A=now;
if(now>0.5&&now<B)B=now;
}
//printf("! %.10f\n",(B-A)*((b[k]-b[k+1]).len()));
return (B-A-1)*((b[k]-b[k+1]).len());
}
double dfs(int l,int r){//point a[l] -> a[r] are not cut
if(l>=r)return 0;
if(v[l][r])return f[l][r];
double ret=inf;
for(int i=l;i<r;i++){
//printf("i=%d cal=%.10f\n",i,cal(i,l-1,r));
up(ret,dfs(l,i)+dfs(i+1,r)+cal(i,l-1,r));
}
v[l][r]=1;
//printf("f[%d][%d]=%.10f\n",l,r,f[l][r]);
return f[l][r]=ret;
}
int main(){
scanf("%d",&n);
for(i=0;i<n;i++)a[i].read();
a[n]=a[0];
scanf("%d",&m);
for(i=0;i<m;i++)b[i].read();
b[m]=b[0];
//cal(6,5,8);
//dfs(6,8);
for(i=0;i<m;i++)pre(wl[i],wr[i],i);
for(i=0;i<m;i++)up(ans,dfs(i+1,i+m)+cal(i,-100,-100));
for(i=0;i<m;i++)ans+=(b[i]-b[i+1]).len();
printf("%.15f",ans);
}
/*
4
-100 -100
-100 100
100 100
100 -100
8
-1 -2
-2 -1
-2 1
-1 2
1 2
2 1
2 -1
1 -2
*/

  

B. Double Clique

一个方案合法当且仅当团点数$\times ($团点数$-1)+$独立集度数和$=$团度数和,且存在可行方案满足团是度数最大的若干个点。

找到可行方案后,要么是团里出去一个点,要么是独立集里出去一个点,要么两边各出去一个点。

时间复杂度$O(n\log n)$。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=200010;
int n,m,i,j,x,y,d[N],s[N];ll ans;
int main(){
scanf("%d%d",&n,&m);
while(m--)scanf("%d%d",&x,&y),d[x]++,d[y]++;
sort(d+1,d+n+1);
reverse(d+1,d+n+1);
for(i=1;i<=n;i++)s[i]=s[i-1]+d[i];
for(i=0;i<=n;i++)if(1LL*i*(i-1)+s[n]-s[i]==s[i]){ans=1;break;}
if(!ans)return puts("0"),0;
for(j=1;j<=i;j++)if(1LL*(i-1)*(i-2)+s[n]-s[i]+d[j]==s[i]-d[j])ans++;
for(j=i+1;j<=n;j++)if(1LL*(i+1)*i+s[n]-s[i]-d[j]==s[i]+d[j])ans++;
for(x=0,j=1;j<=i;j++)if(d[j]==d[i])x++;
for(y=0,j=i+1;j<=n;j++)if(d[j]==d[i])y++;
ans+=1LL*x*y;
printf("%lld",ans%1000000007);
}

  

C. Flashing Fluorescents

注意到答案不超过$n$,枚举答案$ans$,则任何一个可行方案可以由若干个长度互不相等且不超过$ans$的区间异或得到。

设$f[ans][S]$表示长度不超过$ans$能否异或出$S$,枚举当前长度的区间位置转移即可。

时间复杂度$O(2^nn^2)$。

#include<cstdio>
#include<cstring>
int n,i,j,now,S;
bool f[50][1<<16];
char a[50];
int main(){
scanf("%s",a);
n=strlen(a);
for(i=0;i<n;i++)if(a[i]=='0')S^=1<<i;
f[0][S]=1;
while(!f[now][0]){
for(S=0;S<1<<n;S++)f[now+1][S]=f[now][S];
for(i=0;i<n;i++){
int mask=0;
for(j=0;j<now+1&&i+j<n;j++)mask|=1<<(i+j);
for(S=0;S<1<<n;S++)if(f[now][S])f[now+1][S^mask]=1;
}
now++;
}
printf("%d",now);
}

  

D. Missing Gnomes

按题意模拟。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 1e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m;
int a[N], ans[N];
bool use[N];
int main()
{
while(~scanf("%d%d", &n, &m))
{
for(int i = 1; i <= n; ++i)use[i] = 0; for(int i = 1; i <= m; ++i)
{
scanf("%d", &a[i]);
use[a[i]] = 1;
} int x = 1;
int g = 0;
for(int i = 1; i <= m; ++i)
{
while(x < a[i])
{
if(!use[x])ans[++g] = x;
++x;
}
ans[++g] = a[i];
}
while(x <= n)
{
if(!use[x])ans[++g] = x;
++x;
}
for(int i = 1; i <= n; ++i)printf("%d\n", ans[i]);
}
return 0;
} /*
【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */

  

E. Prefix Free Code

建立Trie将字符串映射为数字,从前往后枚举LCP,那么这一位能填的数要小于某个值,且前面没出现过,可以用树状数组加速统计,后面能填的数可以用组合数计算。

时间复杂度$O(n\log n)$。

#include<cstdio>
#include<cstring>
const int N=1000010,P=1000000007;
int n,m,len,i,j,x,son[N][26],v[N],tot,dfn,a[N],cnt;
int bit[N],fac[N],inv[N],ans;
char s[N];
void dfs(int x){
if(v[x])v[x]=++dfn;
for(int i=0;i<26;i++)if(son[x][i])dfs(son[x][i]);
}
inline void ins(int x,int p){for(;x<=n;x+=x&-x)bit[x]+=p;}
inline int ask(int x){int t=0;for(;x;x-=x&-x)t+=bit[x];return t;}
inline int A(int n,int m){return n<m?0:1LL*fac[n]*inv[n-m]%P;}
int main(){
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++){
scanf("%s",s);
len=strlen(s);
for(x=j=0;j<len;j++){
if(!son[x][s[j]-'a'])son[x][s[j]-'a']=++tot;
x=son[x][s[j]-'a'];
}
v[x]=1;
}
dfs(0);
for(fac[0]=i=1;i<=n;i++)fac[i]=1LL*fac[i-1]*i%P;
for(inv[0]=inv[1]=1,i=2;i<=n;i++)inv[i]=1LL*(P-inv[P%i])*(P/i)%P;
for(i=2;i<=n;i++)inv[i]=1LL*inv[i-1]*inv[i]%P;
scanf("%s",s);
for(x=i=0;s[i];i++){
x=son[x][s[i]-'a'];
if(v[x])a[++cnt]=v[x],x=0;
}
ans=1;
for(i=1;i<=n;i++)ins(i,1);
for(i=1;i<=cnt;i++){
ins(a[i],-1);
ans=(1LL*A(n-i,m-i)*ask(a[i])+ans)%P;
}
printf("%d",ans);
}

  

F. Probe Droids

即求斜率第$k$小的坐标,首先特判掉斜率$=0$或者斜率不存在的情况。

在Stern-Brocot Tree上枚举互质数对作为斜率,然后用类欧几里得算法在$O(\log n)$的时间内统计出直线下方的点数,以此来判断往左走还是往右走。

考虑二分往左往右走的拐点位置,则每次询问的复杂度降低至$O(\log^3n)$。

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<iostream>
using namespace std;
typedef long long ll;
ll ansx,ansy;
ll cal(ll a,ll b,ll c,ll n){
if(!a||n<0)return 0;
ll x,y;
if(a>=c||b>=c){
x=cal(a%c,b%c,c,n);
y=a/c*(n*(n+1)/2)+b/c*(n+1)+x;
return y;
}
ll m=(a*n+b)/c;
x=cal(c,c-b-1,a,m-1);
y=n*m-x;
return y;
}
ll calbelow(ll up,ll down,ll n,ll m){
ll lim=min(n*down/up,m);
return cal(up,0,down,lim)+(m-lim)*n;
}
ll calexact(ll up,ll down,ll n,ll m){
return min(n/up,m/down);
}
int check(ll up,ll down,ll n,ll m,ll k){
if(up>n||down>m)return 2;
ll below=calbelow(up,down,n,m);
ll exact=calexact(up,down,n,m);
//cout<<up<<" "<<down<<" "<<below<<" "<<exact<<endl;
//[below-exact+1,below]
if(k>below)return -1;//too small
if(k<below-exact+1)return 1;//too big
return 0;
}
void solve(ll n,ll m,ll k){
ll lu=0,ld=1,ru=1,rd=0,mu,md;
ll A,B;
while(1){
mu=lu+ru;
md=ld+rd;
int ret=check(mu,md,n,m,k);
if(ret==0){
A=mu,B=md;
break;
}
ll l=1,r=n,fin=0;
if(ret<0){
while(l<=r){
ll mid=(l+r)>>1;
if(check(lu+ru*mid,ld+rd*mid,n,m,k)<0)l=(fin=mid)+1;else r=mid-1;
}
lu+=ru*fin,ld+=rd*fin;
}else{
while(l<=r){
ll mid=(l+r)>>1;
if(check(ru+lu*mid,rd+ld*mid,n,m,k)==1)l=(fin=mid)+1;else r=mid-1;
}
ru+=lu*fin,rd+=ld*fin;
}
}
ll below=calbelow(A,B,n,m);
ll exact=calexact(A,B,n,m);
below=below-exact;
k-=below;
ansx=B*k;
ansy=A*k;
//cout<<A<<"/"<<B<<endl;
}
int main(){
ll n,m,q,k;
cin>>n>>m>>q;
while(q--){
cin>>k;
if(k<m){
cout<<"1 "<<k+1<<endl;
continue;
}
if(k>n*m-n){
cout<<k-(n*m-n)+1<<" 1"<<endl;
continue;
}
solve(n-1,m-1,k-(m-1));
cout<<ansy+1<<" "<<ansx+1<<endl;
}
}

  

G. Rainbow Graph

若只有一个限制,满足拟阵。

对于两个限制,则是两个拟阵的交。

首先特判全部都无解的情况,并将全集$E$作为$k=m$的解。

设$M_1$为限制$1$的拟阵,一个方案合法当且仅当在限制$1$下连通,同理定义$M_2$为限制$2$的拟阵。

建立有向图,原图每条边作为一个点,并添加源汇$S$和$T$。

对于上一个$k$的一组最优解$E$中的某条边$x$,如果去掉它后仍然满足$M_1$,则由$S$向$x$连边;若去掉它后仍然满足$M_2$,则由$x$向$T$连边。

对于$E$中某条边$x$和不在$E$中的某条边$y$,若将$x$换成$y$后满足$M_2$,则由$x$向$y$连边;若满足$M_1$,则由$y$向$x$连边。

用SPFA求出$S$到$T$的最短路,就能得到边数恰好减少$1$的最优解。

时间复杂度$O(n^4)$。

#include<cstdio>
const int N=105,M=100000,inf=~0U>>1;
int n,m,i,S,T,x,y,g[N],v[N<<1],nxt[N<<1],ed,vis[N];
int cost[N],col[N],use[N],ans,fin[N];char ch[9];
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs(int x,char ban){
if(vis[x])return;
vis[x]=1;
for(int i=g[x];i;i=nxt[i])if(use[i>>1]&&col[i>>1]!=ban)dfs(v[i],ban);
}
inline bool check(char ban){
int i;
for(i=1;i<=n;i++)vis[i]=0;
dfs(1,ban);
for(i=1;i<=n;i++)if(!vis[i])return 0;
return 1;
}
namespace Matroid{
int g[N],v[M],nxt[M],ed,q[M],h,t,d[N],pre[N],w[N];bool in[N];
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
inline void ext(int x,int y,int z){
if(d[x]<=y)return;
d[x]=y;
pre[x]=z;
if(in[x])return;
q[++t]=x;
in[x]=1;
}
inline bool find(){
int i,j;
S=m+1,T=m+2;
for(ed=0,i=1;i<=T;i++)g[i]=0;
for(i=1;i<=m;i++)if(use[i]){
w[i]=-cost[i];
use[i]^=1;
if(check('R'))add(S,i);
if(check('B'))add(i,T);
use[i]^=1;
}else w[i]=cost[i];
for(i=1;i<=m;i++)if(use[i])for(j=1;j<=m;j++)if(!use[j]){
use[i]^=1,use[j]^=1;
if(check('B'))add(i,j);
if(check('R'))add(j,i);
use[i]^=1,use[j]^=1;
}
for(i=1;i<=T;i++)d[i]=inf,in[i]=0;
q[h=t=1]=S;
d[S]=0,in[S]=1;
while(h<=t){
x=q[h++];
//printf("! %d %d %d\n",x,d[x],pre[x]);
for(i=g[x];i;i=nxt[i])ext(v[i],d[x]+w[v[i]],x);
in[x]=0;
}
if(d[T]==inf)return 0;
ans+=d[T];
while(pre[T]!=S)use[T=pre[T]]^=1;
return 1;
}
}
int main(){
scanf("%d%d",&n,&m);
for(ed=i=1;i<=m;i++){
scanf("%d%d%d%s",&x,&y,&cost[i],ch);
col[i]=ch[0];
add(x,y),add(y,x);
use[i]=1;
ans+=cost[i];
}
if(!check('R')||!check('B')){
for(i=1;i<=m;i++)puts("-1");
return 0;
}
fin[m]=ans;
for(i=m-1;i;i--)if(Matroid::find())fin[i]=ans;
else{
for(x=1;x<=i;x++)fin[x]=-1;
break;
}
for(i=1;i<=m;i++)printf("%d\n",fin[i]);
}

  

H. Recovery

将所有位置都设成$1$,然后贪心配对行列使得满足条件。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 1e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m;
char a[55], b[55];
int aa[55], bb[55];
int ga, gb;
char s[55][55];
int va[55], vb[55];
bool rand(char a[])
{
int n = random() % 4 + 1;
for(int i = 0; i < n; ++i)a[i] = rand() % 2 + '0';
a[n] = 0;
return 1;
} char tt[55][55];
char t[55][55];
bool FLAG;
void BF()
{
FLAG = 0;
int w = n * m;
int top = 1 << w;;
int ansone = -1;
for(int i = 0; i < top; ++i)
{
for(int j = 0; j < w; ++j)
{
tt[n - 1 - j / m][m - 1 - j % m] = '0' + (i >> j & 1);
}
MS(va, 0);
MS(vb, 0);
int one = 0;
for(int j = 0; j < n; ++j)
{
for(int k = 0; k < m; ++k)
{
va[j] += tt[j][k] % 2;
vb[k] += tt[j][k] % 2;
one += tt[j][k] % 2;
}
}
bool flag = 1;
for(int j = 0; j < n; ++j)if(va[j] % 2 != a[j] % 2)
{
flag = 0;
break;
}
for(int j = 0; j < m; ++j)if(vb[j] % 2 != b[j] % 2)
{
flag = 0;
break;
}
if(flag)
{
FLAG = 1;
if(one > ansone)
{
ansone = one;
memcpy(t, tt, sizeof(tt));
}
}
}
} int main()
{
while(~scanf("%s%s", a, b))
//while(rand(a), rand(b))
{
n = strlen(a);
m = strlen(b);
//puts("input"); puts(a); puts(b); MS(s, 0);
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < m; ++j)
{
s[i][j] = '1';
}
}
ga = gb = 0;
for(int i = 0; i < n; ++i)
{
if(m % 2 != a[i] % 2)
{
aa[++ga] = i;
}
}
for(int i = 0; i < m; ++i)
{
if(n % 2 != b[i] % 2)
{
bb[++gb] = i;
}
} //BF();
if(ga + gb & 1)
{
puts("-1");
/*
if(FLAG)
{
puts("NO flag");
while(1);
}
*/
}
else
{
/*
if(!FLAG)
{
puts("NO !flag");
while(1);
}
*/ //printf("ga|gb = %d %d\n", ga, gb); while(ga < gb)aa[++ga] = 0;
while(gb < ga)bb[++gb] = 0;
int g = ga; /*
int g = min(ga, gb);
for(int i = g + 1; i <= ga; ++i)
{
bb[++gb] = 0;
}
for(int i = g + 1; i <= gb; ++i)
{
aa[++ga] = 0;
}
*/ sort(aa + 1, aa + ga + 1);
sort(bb + 1, bb + gb + 1);
/*printf("ga|gb = %d %d\n", ga, gb);
for(int i = 1; i <= ga; ++i)printf("%d ", aa[i]); puts("");
for(int i = 1; i <= gb; ++i)printf("%d ", bb[i]); puts("");
*/
g = max(ga, gb); for(int i = 1; i <= g; ++i)
{
s[aa[i]][bb[i]] = '0';
} for(int i = 0; i < n; ++i)puts(s[i]); /*
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < m; ++j)
{
if(s[i][j] != t[i][j])
{
puts("s[i][j] != t[i][j]"); for(int i = 0; i < n; ++i)puts(s[i]);
for(int i = 0; i < n; ++i)puts(t[i]);
while(1);
}
}
}
*/ /*
MS(va, 0);
MS(vb, 0);
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < m; ++j)
{
va[i] += s[i][j] % 2;
vb[j] += s[i][j] % 2;
}
}
for(int i = 0; i < n; ++i)
{
if(va[i] % 2 != a[i] % 2)
{
puts("NO A");
while(1);
}
}
for(int i = 0; i < m; ++i)
{
if(vb[i] % 2 != b[i] % 2)
{
puts("NO B");
while(1);
}
}
*/
}
}
return 0;
} /*
【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */

  

I. Red Black Tree

设$f[i][j]$表示考虑$i$的子树,里面选了$j$个红点的方案数,转移时只枚举有效的$j$即可得到$O(nm)$的时间复杂度。

#include<cstdio>
const int N=200010,M=1005,P=1000000007;
int n,m,i,x,g[N],nxt[N],size[N],vip[N];
int f[N][M],h[M];
void dfs(int x){
size[x]=vip[x];
//case 1 not choose x
f[x][0]=1;
for(int y=g[x];y;y=nxt[y]){
dfs(y);
for(int j=0;j<=size[x]+size[y]&&j<=m;j++)h[j]=0;
for(int j=0;j<=size[y]&&j<=m;j++)for(int k=0;k<=size[x]&&j+k<=m;k++)
h[j+k]=(1LL*f[y][j]*f[x][k]+h[j+k])%P;
size[x]+=size[y];
for(int j=0;j<=size[x]+size[y]&&j<=m;j++)f[x][j]=h[j];
}
//case 2 choose x
f[x][vip[x]]=(f[x][vip[x]]+1)%P;
}
int main(){
scanf("%d%d",&n,&m);
for(i=2;i<=n;i++){
scanf("%d",&x);
nxt[i]=g[x];g[x]=i;
}
for(i=1;i<=m;i++)scanf("%d",&x),vip[x]=1;
dfs(1);
for(i=0;i<=m;i++)printf("%d\n",f[1][i]);
}

  

J. Winter Festival

因为所有简单环边权和都要是奇数,因此当两个简单环有公共边时不可能满足条件,所以当且仅当图是仙人掌的时候才有解。

设$f[i][j][x][y]$表示考虑DFS生成树中$i$的子树,$i$与$i$父亲的边的边权为$j$,$i$与$i$父亲的边所在环的边权和模$2$为$x$,$i$与$i$父亲的边所在环的非树边边权为$y$的最小代价。

转移时需要通过另一个辅助数组$h[S][j][x][y]$来进行不同子树的合并,其中$j,x,y$含义与$f$相同,而$S$表示$x$点周围存在的边权集合。

时间复杂度$O(n+m)$。

#include<cstdio>
#include<cstdlib>
#define rep(i,n) for(int i=0;i<n;i++)
using namespace std;
const int N=100010,inf=100000000;
int n,m,i,x,y,g[N],v[N<<1],nxt[N<<1],ed;
int mark[N],fa[N],vis[N],dfn;
int f[N][3][2][3];
int dp[1<<3][2][3],h[1<<3][2][3];
int istop[N],isbot[N];
int ban[3][1<<3];
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
inline void up(int&a,int b){a>b?(a=b):0;}
inline void clr(){
rep(S,8)rep(j,2)rep(k,3)h[S][j][k]=inf;
}
inline void go(){
rep(S,8)rep(j,2)rep(k,3)dp[S][j][k]=h[S][j][k];
}
void dfs(int x,int y){
fa[x]=y;
vis[x]=++dfn;
for(int i=g[x];i;i=nxt[i]){
int u=v[i];
if(u==y)continue;
if(!vis[u]){
dfs(u,x);
}else if(vis[u]<vis[x]){
int j=x;
isbot[x]=1;
while(j!=u){
mark[j]++;
if(mark[j]>1){
puts("-1");
exit(0);
}
if(fa[j]==u)istop[j]=1;
j=fa[j];
}
}
}
rep(S,8)rep(j,2)rep(k,3)dp[S][j][k]=inf;
if(!y)dp[0][0][0]=0;
else{
//add the cycle edge
if(isbot[x])rep(c,3)up(dp[1<<c][c&1][c],c);
else dp[0][0][0]=0;
}
for(int i=g[x];i;i=nxt[i]){
int u=v[i];
if(u==y)continue;
if(fa[u]==x){
clr();
if(istop[u]){
rep(S,8)rep(j,2)rep(k,3)if(dp[S][j][k]<inf)
rep(A,3)if(!ban[A][S])rep(B,2)rep(C,3)if(f[u][A][B][C]<inf){
if(B!=1)continue;
if(ban[C][S])continue;
if((A+C)%3==1)continue;
up(h[S|(1<<A)|(1<<C)][j][k],dp[S][j][k]+f[u][A][B][C]);
}
}else if(mark[u]){
rep(S,8)rep(j,2)rep(k,3)if(dp[S][j][k]<inf)
rep(A,3)if(!ban[A][S])rep(B,2)rep(C,3)if(f[u][A][B][C]<inf){
up(h[S|(1<<A)][(j+B)&1][C],dp[S][j][k]+f[u][A][B][C]);
}
}else{
rep(S,8)rep(j,2)rep(k,3)if(dp[S][j][k]<inf)
rep(A,3)if(!ban[A][S])rep(B,1)rep(C,1)if(f[u][A][B][C]<inf){
up(h[S|(1<<A)][j][k],dp[S][j][k]+f[u][A][B][C]);
}
}
go();
}
}
rep(S,3)rep(j,2)rep(k,3)f[x][S][j][k]=inf;
if(y){
//add the father edge
if(mark[x]){
rep(S,8)rep(j,2)rep(k,3)if(dp[S][j][k]<inf)
rep(c,3)if(!ban[c][S])up(f[x][c][(j+c)&1][k],dp[S][j][k]+c);
}else{
rep(S,8)rep(j,2)rep(k,3)if(dp[S][j][k]<inf)
rep(c,3)if(!ban[c][S])up(f[x][c][j][k],dp[S][j][k]+c);
}
}else{
rep(S,8)rep(j,2)rep(k,3)if(dp[S][j][k]<inf)
up(f[x][0][0][0],dp[S][j][k]);
}
}
int main(){
rep(i,3)rep(j,8)rep(k,3)if((j>>k&1)&&(i+k)%3==1)ban[i][j]=1;
scanf("%d%d",&n,&m);
for(ed=i=1;i<=m;i++){
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
int ans=0;
for(i=1;i<=n;i++)if(!vis[i]){
dfs(i,0);
if(f[i][0][0][0]>=inf){
puts("-1");
exit(0);
}
ans+=f[i][0][0][0];
}
printf("%d",ans);
}

  

K. Zoning Houses

若不删除任何点,则答案为区间$x$坐标的极差与$y$坐标极差的较大值。

若删除一个点,则最优方案下一定是删除$x$或者$y$坐标最小或者最大的$4$个点之一,线段树维护即可。

时间复杂度$O(n\log n)$。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef pair<int,int>P;
const int N=100010,M=262150,inf=1000000010;
int n,m,i,x,y,ans;
P xmi[M],xma[M],ymi[M],yma[M];
void build(int x,int a,int b){
if(a==b){
scanf("%d%d",&xmi[x].first,&ymi[x].first);
xmi[x].second=ymi[x].second=a;
xma[x]=xmi[x];
yma[x]=ymi[x];
return;
}
int mid=(a+b)>>1;
build(x<<1,a,mid),build(x<<1|1,mid+1,b);
xmi[x]=min(xmi[x<<1],xmi[x<<1|1]);
xma[x]=max(xma[x<<1],xma[x<<1|1]);
ymi[x]=min(ymi[x<<1],ymi[x<<1|1]);
yma[x]=max(yma[x<<1],yma[x<<1|1]);
}
P askxmi(int x,int a,int b,int c,int d){
if(c>d)return P(inf,0);
if(c<=a&&b<=d)return xmi[x];
int mid=(a+b)>>1;
P t(inf,0);
if(c<=mid)t=askxmi(x<<1,a,mid,c,d);
if(d>mid)t=min(t,askxmi(x<<1|1,mid+1,b,c,d));
return t;
}
P askymi(int x,int a,int b,int c,int d){
if(c>d)return P(inf,0);
if(c<=a&&b<=d)return ymi[x];
int mid=(a+b)>>1;
P t(inf,0);
if(c<=mid)t=askymi(x<<1,a,mid,c,d);
if(d>mid)t=min(t,askymi(x<<1|1,mid+1,b,c,d));
return t;
}
P askxma(int x,int a,int b,int c,int d){
if(c>d)return P(-inf,0);
if(c<=a&&b<=d)return xma[x];
int mid=(a+b)>>1;
P t(-inf,0);
if(c<=mid)t=askxma(x<<1,a,mid,c,d);
if(d>mid)t=max(t,askxma(x<<1|1,mid+1,b,c,d));
return t;
}
P askyma(int x,int a,int b,int c,int d){
if(c>d)return P(-inf,0);
if(c<=a&&b<=d)return yma[x];
int mid=(a+b)>>1;
P t(-inf,0);
if(c<=mid)t=askyma(x<<1,a,mid,c,d);
if(d>mid)t=max(t,askyma(x<<1|1,mid+1,b,c,d));
return t;
}
inline int cal(int x,int y,int z){
return max(
max(askxma(1,1,n,x,z-1).first,askxma(1,1,n,z+1,y).first)-min(askxmi(1,1,n,x,z-1).first,askxmi(1,1,n,z+1,y).first)
,
max(askyma(1,1,n,x,z-1).first,askyma(1,1,n,z+1,y).first)-min(askymi(1,1,n,x,z-1).first,askymi(1,1,n,z+1,y).first)
);
}
int main(){
scanf("%d%d",&n,&m);
build(1,1,n);
while(m--){
scanf("%d%d",&x,&y);
ans=cal(x,y,askxmi(1,1,n,x,y).second);
ans=min(ans,cal(x,y,askxma(1,1,n,x,y).second));
ans=min(ans,cal(x,y,askymi(1,1,n,x,y).second));
ans=min(ans,cal(x,y,askyma(1,1,n,x,y).second));
printf("%d\n",ans);
}
}

  

North American Invitational Programming Contest 2018的更多相关文章

  1. The North American Invitational Programming Contest 2018 D. Missing Gnomes

    A family of nn gnomes likes to line up for a group picture. Each gnome can be uniquely identified by ...

  2. The North American Invitational Programming Contest 2018 H. Recovery

    Consider an n \times mn×m matrix of ones and zeros. For example, this 4 \times 44×4: \displaystyle \ ...

  3. The North American Invitational Programming Contest 2018 E. Prefix Free Code

    Consider nn initial strings of lower case letters, where no initial string is a prefix of any other ...

  4. The North American Invitational Programming Contest 2017 题目

    NAIPC 2017 Yin and Yang Stones 75.39% 1000ms 262144K   A mysterious circular arrangement of black st ...

  5. North American Invitational Programming Contest (NAIPC) 2017

    (待补) A. Pieces of Parentheses 将括号处理完成后排序,方式参加下面的博客.然后做一遍背包即可. 2018 Multi-University Training Contest ...

  6. North American Invitational Programming Contest (NAIPC) 2016

    (待补) A. Fancy Antiques 爆搜. B. Alternative Bracket Notation C. Greetings! D. Programming Team 0/1分数规划 ...

  7. AtCoder SoundHound Inc. Programming Contest 2018 E + Graph (soundhound2018_summer_qual_e)

    原文链接https://www.cnblogs.com/zhouzhendong/p/AtCoder-SoundHound-Inc-Programming-Contest-2018-E.html 题目 ...

  8. ACM International Collegiate Programming Contest, Tishreen Collegiate Programming Contest (2018) Syria, Lattakia, Tishreen University, April, 30, 2018

    ACM International Collegiate Programming Contest, Tishreen Collegiate Programming Contest (2018) Syr ...

  9. German Collegiate Programming Contest 2018​ B. Battle Royale

    Battle Royale games are the current trend in video games and Gamers Concealed Punching Circles (GCPC ...

随机推荐

  1. Mock5 moco框架中post请求如何加入cookies

    接着Mock4中的json文件,再往里面添加一个post 请求. 前面写法不变,后面的请求数据用的是json关键字,返回的response也是json的格式 [ { "description ...

  2. iframe 自适应高度、父子页面传值、回调

    总结一下最近用iframe遇到的问题与解决办法: 结构:主页面main.html,里面套用iframe.iframe不能出现滚动条,自适应子页面高度.内容多了滚动条是main.html页面的. 1.  ...

  3. Docker: 基础介绍 [一]

    一.Docker介绍 Docker是Docker.lnc公司开源的一个基于LXC技术之上构建的Container容器引擎,源代码托管在Github上,基于Go语言并遵从Apache2.0协议开源 Do ...

  4. mysql MHA高可用测试

    [环境介绍] 系统环境:Red Hat Enterprise Linux 7 + 5.7.18 + MHA version 0.57 [测试步骤:自动切换] 当前数据库状态: 系统 IP 主机名 备注 ...

  5. Concurrent下的线程安全集合

    1.ArrayBlockingQueue ArrayBlockingQueue是由数组支持的线程安全的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序.这是一个典型的“有界缓存区”,固定 ...

  6. Java中谈尾递归--尾递归和垃圾回收的比较

    一.首先我们讲讲递归 1.递归的本质是,某个方法中调用了自身,本质还是调用了一个方法,只是这个方法正好是自身而已 2.递归因为是在自身中调用自身,所以会带来以下三个显著特点:    1.调用的是同一个 ...

  7. djang增删改查

    创建表: from django.db import models class Publisher(models.Model): pid = models.AutoField(primary_key ...

  8. 通俗易懂的vuex-demo

    在main.js引入store.js

  9. O2O、B2B、C2C(通俗讲解)

    你在地摊买东西,C2C你去超市买东西,B2C超市找经销商进货,B2B超市出租柜台给经销商卖东西,B2B2C你在网上下载个优惠券去KFC消费,O2O 一:O2O 1.概念: O2O即Online To ...

  10. Lua中的协同程序

    [前言] 协同程序与线程差不多,也就是一条执行序列,拥有自己独立的栈.局部变量和指令指针,同时又与其它协同程序共享全局变量和其它大部分东西.从概念上讲,线程与协同程序的主要区别在于,一个具有多个线程的 ...