2024北京市赛 A.不要玩弄字符串

题意:
n n n个01串。两个人轮流填0或者1,如果某人填完后某个字符串第一次出现,那么这个字符串的分值加到这个人的总分里面,每个人都要最大化自己的分减去另外一个人的分。每次询问从给定的字符串后面开始填起。

思路:
很明显,首先建立AC自动机,然后用状态压缩。

我们设 d p s t a t u , p dp_{statu,p} dpstatu,p表示现在已经在点 p p p,已经填完的字符串状态是 s t a t u statu statu,接下来先手能得到的最大差。

那么肯定是从大到小枚举 s t a t u statu statu,假设现在的状态为 k i ki ki

现在难点在于,如果出现了环应该怎么办。

如果走到了某个节点以后,状态会发生改变,那么我们就把这个点设置成确定的。

如果某一个点能走到的点都是确定的,那么显然它也是确定的。

如果不存在前面的点,那么我们就考虑当前能在已经确定一部分的情况下能到达最大的点,再把这个点变成确定的。

#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define dwn(i,x,y) for(int i=x;i>=y;i--)
#define ll long long
using namespace std;
template<typename T>inline void qr(T &x){
    x=0;int f=0;char s=getchar();
    while(!isdigit(s))f|=s=='-',s=getchar();
    while(isdigit(s))x=x*10+s-48,s=getchar();
    x=f?-x:x;
}
int cc=0,buf[31];
template<typename T>inline void qw(T x){
    if(x<0)putchar('-'),x=-x;
    do{buf[++cc]=int(x%10);x/=10;}while(x);
    while(cc)putchar(buf[cc--]+'0');
}
const int N=160,inf=1e9;
int n,len;char s[N];
int cnt=1,ch[N][2],fail[N],pos[N],statu[N],statu2[N];
int dp[1<<18][N];
vector<int>e[N];int deg[N];
int d[N];
int value[1<<18];
bool v[N];
void add(int id){
    int p=1;
    rep(i,1,len){
        int w=s[i]-'0';
        if(!ch[p][w])ch[p][w]=++cnt;
        p=ch[p][w];
    }
    pos[id]=p;
    statu[p]|=1<<(id-1);
    statu2[p]=statu[p];
}
void getfail(){
    rep(i,0,1)ch[0][i]=1;
    queue<int>q;q.push(1);
    while(q.size()){
        int x=q.front();q.pop();
        int fa=fail[x];
        statu2[x]|=statu2[fa];
        rep(i,0,1){
            int y=ch[x][i];
            if(!y){ch[x][i]=ch[fa][i];continue;}
            fail[y]=ch[fa][i];
            q.push(y);
        }
    }
}
void solve(){
    qr(n);
    int lim=(1<<n)-1;
    rep(i,1,n){
        scanf("%s",s+1);
        len=strlen(s+1);
        add(i);
        int v;qr(v);
        rep(j,0,lim)if(j>>(i-1)&1)value[j]+=v;
    }
    getfail();
    rep(i,1,cnt){
        rep(w,0,1){
            if(ch[i][w]){
                e[ch[i][w]].push_back(i);
            }
        }
    }
    dwn(ki,lim-1,0){
        rep(i,1,cnt){
            d[i]=-inf;
            deg[i]=2;
            v[i]=0;
        }
        queue<int>q;
        priority_queue<pair<int,int> >Q;
        rep(x,1,cnt){
            if((statu2[x]|ki)>ki){
                for(int y:e[x])if((statu2[y]|ki)==ki){
                    if(d[y]<value[statu2[x]^(statu2[x]&ki)]-dp[ki|statu2[x]][x]){
                        d[y]=value[statu2[x]^(statu2[x]&ki)]-dp[ki|statu2[x]][x];
                        Q.push({d[y],y});
                    }
                    if(!--deg[y])q.push(y);
                }
            }
        }
        while(q.size()||Q.size()){
            if(q.size()){
                while(q.size()){
                    int x=q.front();q.pop();
                    if(v[x])continue;
                    v[x]=1;
                    dp[ki][x]=d[x];
                    for(int y:e[x])if((statu2[y]|ki)==ki&&!v[y]){
                        if(d[y]<-d[x]){
                            d[y]=-d[x];
                            Q.push({d[y],y});
                        }
                        if(!--deg[y])q.push(y);
                    }
                }
            }
            else{
                if(Q.size()){
                    int x=Q.top().second;Q.pop();
                    if(v[x])continue;
                    if(d[x]<=0)break;
                    q.push(x);
                }
            }
        }
    }
    int q;qr(q);
    while(q--){
        scanf("%s",s+1);
        len=strlen(s+1);
        int p=1,now=statu2[1];
        rep(i,1,len){
            int w=s[i]-'0';
            p=ch[p][w];
            now|=statu2[p];
        }
        qw(dp[now][p]),puts("");
    }
}
int main(){
    int tt;tt=1;
    while(tt--)solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值