XIII Open Cup named after E.V. Pankratiev. GP of Saratov

A. Box Game

注意到局面总数不超过$50000$,而且每次操作都会改变石子的奇偶性,因此按奇偶可以将状态建成二分图,然后求出最大匹配。

如果状态数是偶数,那么先手必胜,策略就是每次走匹配边,那么对手不能通过匹配边走回来,因此所有匹配边你都能走掉。

如果状态数是奇数,那么如果此时有奇数个石子,也是先手必胜,因为最后对手会遭遇怎么走都会回到以前走过的局面的困境。

选择完先后手之后,每次按匹配边走下去即可。

 

B. Circle of Stones

首先将环倍长,破环成链,那么剩下的子串要满足相邻字符不同且首尾字符不同。

通过双指针求出每一段极长的子串,满足相邻字符不同,设这一段长度为$len$,从$2$到$len$枚举剩下的串的长度$L$,若该子串长度为$len-L+1$的前后缀不能完全匹配,则说明存在距离为$L-1$的位置不同,也就能充当首尾,Hash判断即可。

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

#include<cstdio>
#include<cstring>
typedef long long ll;
const int N=2000010,D=233,P=1000000009;
int C,n,m,i,j,k,len;char a[N],ans[N];ll pow[N],f[N];
inline ll hash(int l,int r){return((f[r]-f[l-1]*pow[r-l+1])%P+P)%P;}
int main(){
  for(pow[0]=i=1;i<N;i++)pow[i]=pow[i-1]*D%P;
  while(~scanf("%s",a+1)){
    n=strlen(a+1);m=n+n;
    for(i=1;i<=n;i++)a[i+n]=a[i];
    for(i=1;i<=m;i++)f[i]=(f[i-1]*D+a[i])%P;
    for(i=0;i<n;i++)ans[i]='0';
    for(i=1;i<=m;i=j+1){
      for(j=i;j<m&&a[j]!=a[j+1];j++);
      len=j-i+1;
      for(k=2;k<=len&&k<=n;k++)if(hash(i,i+len-k)!=hash(j-len+k,j))ans[n-k]='1';
    }
    for(printf("Case %d: ",++C),i=0;i<n-1;i++)putchar(ans[i]);puts("1");
  }
  return 0;
}

  

 

C. Expression with Sets

留坑。

 

D. Heating System

留坑。

 

E. Islands

留坑。

 

F. Longest Two Graphs Common String

设$f[i][j][0]$表示选出的路径里,第一张图中最后一个点是$i$,第二张图中最后一个点是$j$的最大长度。

设$f[i][j][1]$表示选出的路径里,第一张图中最后一个点再往后乱走一步到了$i$,第二张图中最后一个点是$j$的最大长度。

则对于$f[i][j][0]$,只需要枚举指向$j$的边,用$f[i][j'][1]$转移;对于$f[i][j][1]$,只需要枚举指向$i$的边,用$f[i'][j][0]$转移。

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

#include<cstdio>
const int N=505,M=4010;
int T,n1,n2,m,i,j,k,flag,ans,X,Y,q[N*N][2];
int f[N][N][2],pre[N][N][2];
bool v[N][N][2],vis[N][N][2];
struct G{
  int n,g[N],v[M],nxt[M],ed;
  int i,x,y;
  char a[N];
  void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
  void init(int _n,int m){
    n=_n;
    scanf("%s",a+1);
    for(ed=0,i=1;i<=n;i++)g[i]=0;
    while(m--)scanf("%d%d",&x,&y),add(y,x);
  }
}G1,G2;
void dp(int x,int y,int z){
  if(vis[x][y][z])flag=1;
  if(flag)return;
  if(v[x][y][z])return;
  vis[x][y][z]=v[x][y][z]=1;
  int&t=f[x][y][z],&p=pre[x][y][z];
  t=-1,p=0;
  if(z==0){
    if(G1.a[x]==G2.a[y]){
      t=0;
      for(int i=G2.g[y];i;i=G2.nxt[i]){
        int k=G2.v[i];
        dp(x,k,z^1);
        if(f[x][k][z^1]>t)t=f[x][k][z^1],p=k;
      }
      if(~t)t++;
    }
  }else{
    for(int i=G1.g[x];i;i=G1.nxt[i]){
      int k=G1.v[i];
      dp(k,y,z^1);
      if(f[k][y][z^1]>t)t=f[k][y][z^1],p=k;
    }
  }
  vis[x][y][z]=0;
}
int main(){
  while(~scanf("%d%d",&n1,&m)){
    G1.init(n1,m);
    scanf("%d%d",&n2,&m);
    G2.init(n2,m);
    for(i=1;i<=n1;i++)for(j=1;j<=n2;j++)for(k=0;k<2;k++)v[i][j][k]=vis[i][j][k]=0;
    ans=flag=0;
    for(i=1;i<=n1;i++)for(j=1;j<=n2;j++){
      dp(i,j,0);
      if(f[i][j][0]>ans)ans=f[i][j][0],X=i,Y=j;
    }
    printf("Case %d: ",++T);
    if(flag)puts("-1");else{
      printf("%d\n",ans);
      for(i=ans;i;i--){
        q[i][0]=X,q[i][1]=Y;
        Y=pre[X][Y][0];
        X=pre[X][Y][1];
      }
      for(i=1;i<=ans;i++)printf("%d%c",q[i][0],i<ans?' ':'\n');
      for(i=1;i<=ans;i++)printf("%d%c",q[i][1],i<ans?' ':'\n');
    }
  }
  return 0;
}

  

 

G. Lyndon Words

找规律构造,可以$O(n)$生成字典序下一个,暴力生成出字典序前$r$小的所有串即可。

时间复杂度$O(nr)$。

 

H. Temperature

状压DP。

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

 

I. Editor 2

留坑。

 

J. Tourist Problem

留坑。

 

K. Ant versus Woodpecker

显然答案就是虚树上度数$>1$的点的深度的最大值,用ETT维护dfs序并支持查询lca即可。

时间复杂度$O((m+\sum k)\log n)$。

#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
const int N=400010;
int C,n,m,i,x,g[N],Nxt[N];
int vis[N],a[N],dfn[N],deg[N],t,q[N];
int id[N];
int d[N],st[N],en[N],pre[N],nxt[N],val[N],md[N],size[N],son[N][2],f[N],tot,root;
set<int>T;
inline void up(int x){
  md[x]=val[x];
  size[x]=1;
  if(son[x][0]){
    if(d[md[son[x][0]]]<d[md[x]])md[x]=md[son[x][0]];
    size[x]+=size[son[x][0]];
  }
  if(son[x][1]){
    if(d[md[son[x][1]]]<d[md[x]])md[x]=md[son[x][1]];
    size[x]+=size[son[x][1]];
  }
}
inline void rotate(int x){
  int y=f[x],w=son[y][1]==x;
  son[y][w]=son[x][w^1];
  if(son[x][w^1])f[son[x][w^1]]=y;
  if(f[y]){
    int z=f[y];
    if(son[z][0]==y)son[z][0]=x;
    if(son[z][1]==y)son[z][1]=x;
  }
  f[x]=f[y];son[x][w^1]=y;f[y]=x;up(y);
}
inline void splay(int x,int w){
  while(f[x]!=w){
    int y=f[x];
    if(f[y]!=w){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}
    rotate(x);
  }
  if(!w)root=x;
  up(x);
}
inline int getrank(int x){
  splay(x,0);
  return size[son[x][0]];
}
inline void addnode(int x,int y){//x father is y
  d[x]=d[y]+1;
  int z=nxt[st[y]];
  splay(st[y],0);
  splay(z,root);
  son[z][0]=++tot;
  val[tot]=x;
  f[tot]=z;
  st[x]=tot;
  id[tot]=x;
  son[st[x]][1]=++tot;
  f[tot]=st[x];
  val[tot]=y;
  en[x]=tot;
  up(tot);
  up(st[x]);
  up(z);
  up(root);
  nxt[root]=st[x];
  nxt[st[x]]=en[x];
  nxt[en[x]]=z;
  pre[z]=en[x];
  pre[en[x]]=st[x];
  pre[st[x]]=root;
}
void dfsdel(int x){
  if(!x)return;
  if(id[x])T.insert(id[x]);
  dfsdel(son[x][0]);
  dfsdel(son[x][1]);
}
inline void delsubtree(int x){
  splay(pre[st[x]],0);
  splay(nxt[en[x]],root);
  int z=son[root][1];
  nxt[root]=z;
  pre[z]=root;
  dfsdel(son[z][0]);
  son[z][0]=0;
  up(z);
  up(root);
}
void show(int x){
  if(!x)return;
  show(son[x][0]);
  printf("%d ",val[x]);
  show(son[x][1]);
}
inline int lca(int x,int y){
  if(x==y)return x;
  if(x==1||y==1)return 1;
  if(getrank(st[x])>getrank(st[y]))swap(x,y);
  splay(pre[st[x]],0);
  splay(nxt[st[y]],root);
  return md[son[son[root][1]][0]];
}
int build(int l,int r,int fa){
  int mid=(l+r)>>1;
  f[mid]=fa;
  if(l<mid)son[mid][0]=build(l,mid-1,mid);
  if(r>mid)son[mid][1]=build(mid+1,r,mid);
  up(mid);
  return mid;
}
inline bool cmp(int x,int y){return dfn[x]<dfn[y];}
void dfs(int x){
  for(int i=g[x];i;i=Nxt[i]){
    d[i]=d[x]+1;
    st[i]=++tot;
    val[tot]=i;
    dfs(i);
    en[i]=++tot;
    val[tot]=x;
  }
}
inline int getid(){
  int x=*T.begin();
  T.erase(x);
  return x;
}
int main(){
  while(~scanf("%d",&n)){
    st[1]=1;
    val[1]=1;
    tot=1;
    for(i=2;i<=n;i++){
      scanf("%d",&x);
      Nxt[i]=g[x];
      g[x]=i;
    }
    dfs(1);
    en[1]=++tot;
    val[tot]=1;
    for(i=1;i<tot;i++){
      pre[i+1]=i;
      nxt[i]=i+1;
    }
    for(i=1;i<=n;i++)id[st[i]]=i;
    root=build(1,tot,0);
    int mm;
    scanf("%d",&mm);
    int lim=n+mm;
    T.clear();
    for(i=n+1;i<=n+mm;i++)T.insert(i);
    int last=0;
    printf("Case %d:",++C);
    while(mm--){
      char op[5];
      scanf("%s",op);
      if(op[0]=='+'){
        int x;
        scanf("%d",&x);
        int y=getid();
        x+=last;
        addnode(y,x);
      }
      if(op[0]=='-'){
        int x;
        scanf("%d",&x);
        delsubtree(x);
      }
      if(op[0]=='?'){
        //show(root);puts("");
        scanf("%d",&m);
        int cnt=1;
        a[1]=1;
        vis[1]=1;
        dfn[1]=getrank(st[1]);
        while(m--){
          scanf("%d",&x);
          if(!vis[x]){
            a[++cnt]=x,vis[x]=1;
            dfn[x]=getrank(st[x]);
          }
        }
        //for(i=1;i<=cnt;i++)printf("%d ",a[i]);puts("");
        sort(a+1,a+cnt+1,cmp);
        int now=cnt;
        for(i=1;i<cnt;i++)if(!vis[x=lca(a[i],a[i+1])]){
          vis[a[++now]=x]=1;
          dfn[x]=getrank(st[x]);
        }
        sort(a+1,a+now+1,cmp);
        //for(i=1;i<=now;i++)printf("%d ",a[i]);puts("");
        last=-1;
        for(q[t=1]=1,i=2;i<=now;q[++t]=a[i++]){
          while(lca(a[i],q[t])!=q[t])t--;
          deg[q[t]]++;
          if(deg[q[t]]>1)last=max(last,d[q[t]]);
          //printf("edge %d %d\n",q[t],a[i]);
        }
        printf(" %d",last);
        for(i=1;i<=now;i++)vis[a[i]]=deg[a[i]]=0;
      }
    }
    puts("");
    for(i=1;i<=lim;i++)st[i]=en[i]=d[i]=g[i]=0;
    for(i=0;i<=tot+1;i++)id[i]=pre[i]=nxt[i]=val[i]=md[i]=size[i]=son[i][0]=son[i][1]=f[i]=0;
    tot=root=0;
  }
  return 0;
}

  

 

posted @ 2016-07-24 20:42  Claris  阅读(362)  评论(0编辑  收藏  举报