【XSY3990】Alice 和 Bob 双在玩游戏(博弈,dp,拓扑,背包)

题面

Alice 和 Bob 双在玩游戏

题解

注意到这里一个人无法操作后,另一个人也不一定无法操作(即不像普通的取石子游戏一样),所以考虑转化一下他们各自的最优策略:双方都想让自己比对方尽可能多移动一些步。

然后注意到每一颗石子都是相互独立的,即一个点上多一颗石子不会影响其他石子。

所以考虑设 \(f_i\) 表示:若在 \(i\) 节点上多放一颗棋子,在双方都使用最优策略的情况下,Alice 能比 Bob 最多多走多少步。(\(f_i\) 可能是负数,因为 \(i\) 节点的颜色是不确定的)

容易得到转移方程:

\[f_u= \begin{cases} \max(0,\max\limits_{(u,v)}f_v+1)&\text{if }col_u=\operatorname{white}\\ \min(0,\min\limits_{(u,v)}f_v-1)&\text{if }col_u=\operatorname{black}\\ \end{cases} \]

要对 \(0\)\(\max/\min\) 是因为我可以选择不移动新增加的这一枚棋子,这样相当于没有变化。

那么直接建反向图拓扑排序转移即可。

然后题目问的其实是在在双方都使用最优策略的情况下,有多少种情况使得 Alice 比 Bob 多走的步数为正数。

然后现在每一个节点如果放了棋子对多走的步数的贡献也已经算出来了(即 \(f_i\)),所以背包转移即可。

代码如下:

#include<bits/stdc++.h>
 
#define N 310
 
using namespace std;
 
namespace modular
{
    const int mod=998244353;
    inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
    inline int mul(int x,int y){return 1ll*x*y%mod;}
}using namespace modular;
 
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^'0');
        ch=getchar();
    }
    return x*f;
}
 
int n,m,deg[N];
int cnt,head[N],to[N*N],nxt[N*N];
int f[N],dp[N][N*N*2];
char s[N];
bool col[N];
 
queue<int>q;
 
void adde(int u,int v)
{
    to[++cnt]=v;
    nxt[cnt]=head[u];
    head[u]=cnt;
}
 
void tuopu()
{
    for(int i=1;i<=n;i++)
        if(!deg[i]) q.push(i);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(col[v]) f[v]=min(f[v],f[u]-1);
            else f[v]=max(f[v],f[u]+1);
            deg[v]--;
            if(!deg[v]) q.push(v);
        }
    }
}
 
int main()
{
    n=read(),m=read();
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
        col[i]=(s[i]=='B');
    for(int i=1;i<=m;i++)
    {
        int u=read(),v=read();
        adde(v,u),deg[u]++;
    }
    tuopu();
    dp[0][n*n]=1;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<=n*n*2;j++)
        {
            if(!dp[i][j]) continue;
            dp[i+1][j+f[i+1]]=add(dp[i+1][j+f[i+1]],dp[i][j]);
            dp[i+1][j]=add(dp[i+1][j],dp[i][j]);
        }
    }
    int ans=0;
    for(int i=n*n+1;i<=n*n*2;i++)
        ans=add(ans,dp[n][i]);
    printf("%d\n",ans);
    return 0;
}
/*
5 4
wwwww
1 2
2 3
3 4
4 5
*/
/*
10 15
BWBWBBWWBW
1 2
1 5
1 10
2 6
2 8
3 6
3 7
4 10
5 6
5 7
5 8
6 8
6 9
7 10
8 9
*/
posted @ 2022-10-31 07:23  ez_lcw  阅读(58)  评论(0编辑  收藏  举报