XVII Open Cup named after E.V. Pankratiev. Grand Prix of America (NAIPC-2017)
A. Pieces of Parentheses
将括号串排序,先处理会使左括号数增加的串,这里面先处理减少的值少的串;再处理会使左括号数减少的串,这里面先处理差值较大的串。确定顺序之后就可以DP了。
时间复杂度$O(n^3)$。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=310,inf=1000000; int n,i,j,m,all,f[N*N],g[N*N];char s[N]; struct P{ int x,y,w; int type,p; void cal(){ if(y>=0){ type=1; p=-x; }else{ type=0; p=y-x; } } }a[N]; inline bool cmp(const P&a,const P&b){ if(a.type!=b.type)return a.type>b.type; if(a.type==1)return a.p<b.p; return a.p>b.p; } int main(){ scanf("%d",&n); for(i=1;i<=n;i++){ scanf("%s",s); m=strlen(s); for(j=0;j<m;j++){ if(s[j]=='(')a[i].y++;else a[i].y--; a[i].x=min(a[i].x,a[i].y); } a[i].w=m; a[i].cal(); all+=m; } sort(a+1,a+n+1,cmp); for(i=1;i<=all;i++)f[i]=-inf; for(i=1;i<=n;i++){ for(j=0;j<=all;j++)g[j]=f[j]; for(j=0;j<=all;j++)if(j+a[i].x>=0&&f[j]>=0)g[j+a[i].y]=max(g[j+a[i].y],f[j]+a[i].w); for(j=0;j<=all;j++)f[j]=g[j]; } printf("%d",f[0]); }
B. Stars in a Can
求出三维凸包,枚举一个面,求出距离这个面最远的点作为圆柱体的高,然后将所有点投影到平面上求最小圆覆盖作为底面即可。
时间复杂度$O(n^2)$。
C. Stretching Streamers
$f[i][j][k]$表示$[i,j]$区间,$(i,j)$这条边存在情况为$k$时的生成树个数,然后区间DP即可。
时间复杂度$O(n^3)$。
#include<stdio.h> #include<algorithm> #include<math.h> #include<string.h> #include<string> #include<vector> #include<set> #include<map> #include<queue> #include<time.h> #include<assert.h> #include<iostream> using namespace std; typedef long long LL; typedef pair<int,int>pi; const int Maxn=313,mod=1e9+7; int n; int g0[Maxn][Maxn][2]; int g1[Maxn][Maxn][2]; int a[Maxn]; int ok[Maxn][Maxn]; inline void up(int &x,int y){ x+=y;if(x>=mod)x-=mod; } int main(){ scanf("%d",&n); for(int i=0;i<n;i++)scanf("%d",a+i); for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ ok[i][j]=__gcd(a[i],a[j])>1; } } for(int i=0;i<n;i++)g0[i][i][0]=g1[i][i][0]=1; for(int len=2;len<=n;len++){ for(int l=0;l<n;l++){ int r=l+len-1; if(r>=n)break; g0[l][r][0]=g0[l][r][1]=0; for(int br=l;br<r;br++){ up(g0[l][r][0],1LL*g0[l][br][1]*g0[br][r][0]%mod); if(ok[l][r])up(g0[l][r][1],1LL*g0[l][br][0]*g1[br+1][r][0]%mod); /* if(l==0&&r==1){ printf("br=%d %d %d\n",br,g0[l][r][0],g0[l][r][1]); } */ } up(g0[l][r][0],g0[l][r][1]); /* if(l==0&&r==2){ printf("%d %d\n",g0[l][r][0],g0[l][r][1]); } */ g1[l][r][0]=g1[l][r][1]=0; for(int bl=r;bl>l;bl--){ up(g1[l][r][0],1LL*g1[bl][r][1]*g1[l][bl][0]%mod); if(ok[l][r])up(g1[l][r][1],1LL*g1[bl][r][0]*g0[l][bl-1][0]%mod); } up(g1[l][r][0],g1[l][r][1]); //printf("l=%d r=%d g=%d\n",l,r,g0[l][r][0]); } } printf("%d\n",g0[0][n-1][0]); return 0; }
D. Heaps from Trees
$f[i][j]$表示$i$的子树内选择点集的权值最大值为$j$时最多选几个点,用dsu on tree配合线段树转移即可。
时间复杂度$O(n\log^2n)$。
#include<cstdio> #include<algorithm> using namespace std; const int N=200010,M=N*20; int n,i,x,g[N],nxt[N],size[N],son[N],a[N],b[N],q[N],cnt; int l[M],r[M],v[M],tag[M],root[N]; int rub[M],cur; void clear(int x){ if(!x)return; clear(l[x]); clear(r[x]); rub[++cur]=x; } inline int newnode(){ int x=rub[cur--]; l[x]=r[x]=v[x]=tag[x]=0; return x; } inline void tag1(int x,int p){ if(!x)return; tag[x]+=p; v[x]+=p; } inline void pb(int x){ if(tag[x])tag1(l[x],tag[x]),tag1(r[x],tag[x]),tag[x]=0; } inline void up(int x){ v[x]=max(v[l[x]],v[r[x]]); } void ins(int&x,int a,int b,int c,int d){ if(!x)x=newnode(); pb(x); if(a==b){ v[x]=max(v[x],d); return; } int mid=(a+b)>>1; if(c<=mid)ins(l[x],a,mid,c,d); else ins(r[x],mid+1,b,c,d); up(x); } int ask(int x,int a,int b,int c,int d){ if(!x)return 0; if(c<=a&&b<=d)return v[x]; pb(x); int mid=(a+b)>>1,t=0; if(c<=mid)t=ask(l[x],a,mid,c,d); if(d>mid)t=max(t,ask(r[x],mid+1,b,c,d)); return t; } void add(int x,int a,int b,int c,int d,int p){ if(!x)return; if(c<=a&&b<=d){tag1(x,p);return;} pb(x); int mid=(a+b)>>1; if(c<=mid)add(l[x],a,mid,c,d,p); if(d>mid)add(r[x],mid+1,b,c,d,p); up(x); } inline int lower(int x){ int l=1,r=n,mid,t; while(l<=r)if(b[mid=(l+r)>>1]<=x)l=(t=mid)+1;else r=mid-1; return t; } void dfs(int x){ size[x]=1; for(int i=g[x];i;i=nxt[i]){ dfs(i); size[x]+=size[i]; if(size[i]>size[son[x]])son[x]=i; } } void go(int x){ q[++cnt]=a[x]; for(int i=g[x];i;i=nxt[i])go(i); } inline void merge(int x,int y){ cnt=0; go(y); sort(q+1,q+cnt+1); q[cnt+1]=n+1; static int A[N],B[N]; for(int i=1;i<=cnt;i++){ int o=q[i]; A[i]=ask(root[y],1,n,o,o); B[i]=ask(root[x],1,n,1,o); } for(int i=1,t=0;i<=cnt;i++){ int o=q[i]; t=max(t,A[i]); if(o!=q[i+1])add(root[x],1,n,o,q[i+1]-1,t); } clear(root[y]); for(int i=1;i<=cnt;i++){ ins(root[x],1,n,q[i],A[i]+B[i]); } } void dfs2(int x){ if(son[x]){ dfs2(son[x]); root[x]=root[son[x]]; } for(int i=g[x];i;i=nxt[i])if(i!=son[x]){ dfs2(i); merge(x,i); } int o=0; if(a[x]>1)o=ask(root[x],1,n,1,a[x]-1); ins(root[x],1,n,a[x],o+1); } int main(){ cur=M-1; for(i=1;i<=cur;i++)rub[i]=i; scanf("%d",&n); for(i=1;i<=n;i++){ scanf("%d%d",&a[i],&x); if(x){ nxt[i]=g[x]; g[x]=i; } b[i]=a[i]; } sort(b+1,b+n+1); for(i=1;i<=n;i++)a[i]=lower(a[i]); dfs(1); dfs2(1); printf("%d",ask(root[1],1,n,1,n)); }
E. Blazing New Trails
二分参数$mid$,将所有特殊边的权值加上$mid$求MST,使得得到的生成树恰好有$K$条特殊边即可。
时间复杂度$O(n\log^2n)$。
F. Incremental Double Free Strings
留坑。
G. Apple Market
二维ST表优化建图的网络流。
#include<stdio.h> #include<algorithm> #include<math.h> #include<string.h> #include<string> #include<vector> #include<set> #include<map> #include<queue> #include<time.h> #include<assert.h> #include<iostream> using namespace std; typedef long long LL; typedef pair<int,int>pi; const int Maxn=55; const int MAXN = 1000005 ; const int MAXE = 3000005 ; const LL INF = 1LL<<60 ; struct Edge { int v , n ; LL c; Edge () {} Edge ( int v , LL c , int n ) : v ( v ) , c ( c ) , n ( n ) {} } ; Edge E[MAXE] ; int H[MAXN] , cntE ; int d[MAXN] , cur[MAXN] , gap[MAXN] , pre[MAXN] ; int Q[MAXN] , head , tail ; int s , t , nv ; LL flow ; int a[55][55][6][6]; int ori[55][55]; int cl[66]; int idx; void init () { memset ( H , -1 , sizeof H ) ; cntE = 0 ; } void add ( int u , int v , LL c ) { if(v==0)while(1); E[cntE] = Edge ( v , c , H[u] ) ; H[u] = cntE ++ ; E[cntE] = Edge ( u , 0 , H[v] ) ; H[v] = cntE ++ ; } void rev_bfs () { memset ( d , -1 , sizeof d ) ; memset ( gap , 0 , sizeof gap ) ; d[t] = 0 ; gap[d[t]] = 1 ; head = tail = 0 ; Q[tail ++] = t ; while ( head != tail ) { int u = Q[head ++] ; for ( int i = H[u] ; ~i ; i = E[i].n ) { int v = E[i].v ; if ( d[v] == -1 ) { d[v] = d[u] + 1 ; gap[d[v]] ++ ; Q[tail ++] = v ; } } } } int isap () { flow = 0 ; rev_bfs () ; memcpy ( cur , H , sizeof cur ) ; int u = pre[s] = s , i , mv ; while ( d[s] < nv ) { if ( u == t ) { LL f = INF ; for ( i = s ; i != t ; i = E[cur[i]].v ) { if ( f > E[cur[i]].c ) { f = E[cur[i]].c ; u = i ; } } flow += f ; for ( i = s ; i != t ; i = E[cur[i]].v ) { E[cur[i]].c -= f ; E[cur[i] ^ 1].c += f ; } } for ( i = cur[u] ; ~i ; i = E[i].n ) { if ( E[i].c && d[u] == d[E[i].v] + 1 ) break ; } if ( ~i ) { cur[u] = i ; pre[E[i].v] = u ; u = E[i].v ; } else { if ( 0 == -- gap[d[u]] ) break ; mv = nv ; for ( i = H[u] ; ~i ; i = E[i].n ) { int v = E[i].v ; if ( E[i].c && mv > d[v] ) { cur[u] = i ; mv = d[v] ; } } d[u] = mv + 1 ; gap[d[u]] ++ ; u = pre[u] ; } } return flow ; } void precal(int n,int m){ for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ a[i][j][0][0]=++idx; //a[i][j][0][0]=ori[i][j]; } } for(int i=0;(1<<i)<=n;i++){ for(int j=0;(1<<j)<=m;j++){ if(!i&&!j)continue; for(int x=0;x+(1<<i)<=n;x++){ for(int y=0;y+(1<<j)<=m;y++){ a[x][y][i][j]=++idx; if(!j){ add(a[x][y][i][j],a[x][y][i-1][j],INF); add(a[x][y][i][j],a[x+(1<<(i-1))][y][i-1][j],INF); //a[x][y][i][j]=max(a[x][y][i-1][j],a[x+(1<<(i-1))][y][i-1][j]); } else{ add(a[x][y][i][j],a[x][y][i][j-1],INF); add(a[x][y][i][j],a[x][y+(1<<j-1)][i][j-1],INF); //a[x][y][i][j]=max(a[x][y][i][j-1],a[x][y+(1<<(j-1))][i][j-1]); } } } } } } void ask(int o,int x1,int y1,int x2,int y2){ int len1=x2-x1+1,len2=y2-y1+1; int tx=cl[len1],ty=cl[len2]; len1=tx;len2=ty; add(o,a[x1][y1][tx][ty],INF); add(o,a[x2-(1<<len1)+1][y1][tx][ty],INF); add(o,a[x1][y2-(1<<len2)+1][tx][ty],INF); add(o,a[x2-(1<<len1)+1][y2-(1<<len2)+1][tx][ty],INF); } int check(int x1,int y1,int x2,int y2){ int ret=0; for(int i=x1;i<=x2;i++) for(int j=y1;j<=y2;j++){ ret=max(ret,ori[i][j]); } return ret; } int main(){ init () ; for(int i=2;i<55;i++){ cl[i]=cl[i>>1]+1; } int n , m , k ; scanf ( "%d%d%d" , &n , &m , &k ) ; for ( int i = 0 ; i < n ; ++ i ) { for ( int j = 0 ; j < m ; ++ j ) { scanf ( "%d" , &ori[i][j] ) ; } } precal ( n , m ) ; s = 0 ; for ( int i = 1 ; i <= k ; ++ i ) { int x1 , x2 , y1 , y2 , val ; scanf ( "%d%d%d%d%d" , &x1 , &x2 , &y1 , &y2 , &val ) ; if ( x1 > x2 ) swap ( x1 , x2 ) ; if ( y1 > y2 ) swap ( y1 , y2 ) ; -- x1 ; -- y1 ; -- x2 ; -- y2 ; ++ idx ; add ( s , idx , val ) ; ask ( idx , x1 , y1 , x2 , y2 ) ; } t = ++ idx ; nv = t + 1 ; for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ add ( a[i][j][0][0] , t , ori[i][j] ) ; //a[i][j][0][0]=ori[i][j]; } } isap () ; //printf("%d %d\n",idx,cntE); printf ( "%lld\n" , flow ) ; if ( scanf ( "%d%d%d" , &n , &m , &k ) != -1 ) while ( 1 ) ; /* int q=100; while(q--){ int x1=rand()%n,y1=rand()%m,x2=rand()%n,y2=rand()%m; if(x1>x2)swap(x1,x2); if(y1>y2)swap(y1,y2); int ans1=ask(x1,y1,x2,y2); int ans2=check(x1,y1,x2,y2); if(ans1!=ans2){ printf("%d %d %d %d ans=%d %d\n",x1,y1,x2,y2,ans1,ans2); } else puts("ok"); } */ return 0; }
H. Maximum Color Clique
留坑。
I. Ski Resort
留坑。
J. Yin and Yang Stones
按题意模拟。
#include <bits/stdc++.h> using namespace std ; const int MAXN = 100005 ; char s[MAXN] ; void solve () { int x = 0 , y = 0 ; for ( int i = 0 ; s[i] ; ++ i ) { if ( s[i] == 'W' ) ++ x ; else ++ y ; } printf ( "%d\n" , x == y ) ; } int main () { while ( ~scanf ( "%s" , s ) ) solve () ; return 0 ; }
K. Unbalanced Parentheses
留坑。