Codeforces 1785 E Infinite Game 题解 (图论,自动机,dp)

题目链接

假设现在字符串s中已经没有问号,我们来确定这时的答案。我们建立一个4个点的有向图(可以看成一个自动机),4个点分别代表每个set内部的比分:0:0, 1:0, 0:1, 1:1。在其中连一些边,比如从1:0往1:1连一条权值为0的边,代表放一个字符b时会走这条边,且对Alice赢的set数贡献为0;从1:0往0:0连一条权值为1的边,代表放一个字符a时会走这条边,且对Alice赢的set数的贡献为1。一共连8条这样的边,每个点的出度为2。然后令初始位置为0:0,我们从左到右遍历s,当加入一个字符时就在图中走对应的边,把边权加入答案,并把当前位置沿着这条边移动一次。令\(to_i\)表示以状态i为初始位置,遍历完整个s后得到的状态;\(val_i\)表示以状态i为初始位置,遍历完s的过程中给答案加上的值的和,其中\(i\in \{0:0,1:0,0:1,1:1 \}\)。我们把\(i\to to_i\)连边,建出一个新图。新图的形态是一个内向基环树,由于这题要求我们不断重复s,也就相当于在新图上从0:0开始不断地走,并把经过的每个点的\(val_i\)加入答案。在这个新图中,从0:0出发一定会到达基环,并在基环上不断绕圈。令sum表示基环上所有点的\(val_i\)之和,如果\(sum>0\),显然当走的步数趋于无穷时Alice赢的set数更多;如果\(sum<0\),显然Bob赢的set数更多;否则\(sum=0\),此时无论你在到达基环之前经过的点的\(val_i\)之和是正是负,当走的步数趋于无穷时,Alice与Bob赢的set数之差都是趋于0的,所以此时是平局。

然后来看原问题,考虑dp。你可以在dp状态中记录有向图中的4个点每个分别可以走到哪个位置,以及它们的\(val_i\),但是这样复杂度非常高,可能是\(n^5\)左右(但是tourist说实现得好是可以过的)。注意到我们只对新图中基环里所有点的\(val_i\)之和感兴趣,而不是对每个状态的\(val_i\)都感兴趣。所以可以先枚举基环中含有的点的mask,对于每次枚举用一次dp算出答案。\(dp_{i,u1,u2,u3,u4,sum}\)表示确定了s中的前i个位置填什么,此时0:0,1:0,0:1,1:1分别能走到\(u1,u2,u3,u4\),在mask中的状态的\(val\)之和为\(sum\)的方案数。转移就枚举s中接下来一个位置填什么,然后根据那个4个点8条边的有向图的信息更新dp状态就可以了,转移是\(O(1)\)的。最后计算答案的时候,枚举所有\(u1,u2,u3,u4\)的组合,检查它们确定的基环树的基环中的点是否就是mask中那些,如果是就更新答案。

时间复杂度\(O(2^4\cdot 4^4\cdot n^2)\)。看上去计算量挺大,其实如果只访问可能有效的状态,是跑得很快的。dp数组要滚动,不然空间可能会爆掉。

点击查看代码
#include <bits/stdc++.h>

#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <LL,LL>
#define fi first
#define se second
#define pb push_back
#define mpr make_pair

void fileio()
{
  #ifdef LGS
  freopen("in.txt","r",stdin);
  freopen("out.txt","w",stdout);
  #endif
}
void termin()
{
  #ifdef LGS
  std::cout<<"\n\nEXECUTION TERMINATED";
  #endif
  exit(0);
}

using namespace std;

const LL MOD=998244353,bas=404;

void add(LL &x,LL y){x+=y;if(x>=MOD) x-=MOD;}

string s;
LL dp[2][810][4][4][4][4],ans1=0,ans2=0,ans3=0,curto[4];
pii to[4][2];

int getCyc()
{
  bool vis[4];rep(i,4) vis[i]=false;
  vector <int> pth;int cur=0;
  while(true)
  {
    if(vis[cur])
    {
      rep(i,pth.size()) if(pth[i]==cur)
      {
        int ret=0;
        for(int j=i;j<pth.size();++j) ret|=(1<<pth[j]);
        return ret;
      }
    }
    vis[cur]=true;pth.pb(cur);
    cur=curto[cur];
  }
}

int main()
{
  fileio();
  
  to[0][0]=mpr(1,0);to[0][1]=mpr(2,0);
  to[1][0]=mpr(0,1);to[1][1]=mpr(3,0);
  to[2][0]=mpr(3,0);to[2][1]=mpr(0,-1);
  to[3][0]=mpr(0,1);to[3][1]=mpr(0,-1);
  cin>>s;
  rep(incyc,1<<4) if(incyc>0)
  {
    LL cnt=__builtin_popcount(incyc);
    rep(i,2) rep(j,809) rep(k1,4) rep(k2,4) rep(k3,4) rep(k4,4) dp[i][j][k1][k2][k3][k4]=0;
    dp[0][bas][0][1][2][3]=1;
    rep(ii,s.size())
    {
      int i=ii&1,lb=bas-(ii+4)/2*cnt,ub=bas+(ii+4)/2*cnt;
      lb=max(lb,0);ub=min(ub,809);
      for(int j=lb;j<=ub;++j) rep(k1,4) rep(k2,4) rep(k3,4) rep(k4,4)
      {
        LL &val=dp[i][j][k1][k2][k3][k4];
        if(val==0) continue;
        int nk1,nk2,nk3,nk4;
        rep(nxt,2) if(s[ii]=='?'||s[ii]==nxt+'a')
        {
          LL nj=j;
          if(incyc&1) nj+=to[k1][nxt].se;nk1=to[k1][nxt].fi;
          if(incyc&2) nj+=to[k2][nxt].se;nk2=to[k2][nxt].fi;
          if(incyc&4) nj+=to[k3][nxt].se;nk3=to[k3][nxt].fi;
          if(incyc&8) nj+=to[k4][nxt].se;nk4=to[k4][nxt].fi;
          add(dp[i^1][nj][nk1][nk2][nk3][nk4],val);
        }
        val=0;
      }
    }
    rep(k1,4) rep(k2,4) rep(k3,4) rep(k4,4)
    {
      curto[0]=k1;curto[1]=k2;curto[2]=k3;curto[3]=k4;
      if(getCyc()==incyc)
      {
        rep(j,809)
        {
          if(j<bas) (ans3+=dp[s.size()&1][j][k1][k2][k3][k4])%=MOD;
          else if(j==bas) (ans2+=dp[s.size()&1][j][k1][k2][k3][k4])%=MOD;
          else (ans1+=dp[s.size()&1][j][k1][k2][k3][k4])%=MOD;
        }
      }
    }
  }
  cout<<ans1<<endl<<ans2<<endl<<ans3<<endl;

  termin();
}
posted @ 2023-03-05 12:16  LegendStane  阅读(313)  评论(0)    收藏  举报