题目分析

称可以放置障碍的点为障碍点,假设 1 号点是一个不可防止障碍的障碍点

将原图拓扑排序,拓扑序前一半障碍点和后一半障碍点分开考虑,那么一条路径一定是:

0号点->后一半障碍点中的某一个(并且它是第一个遇到的后一半障碍点)->1 号点

分开考虑后,前一半和后一半障碍点可以分别枚举它们放置障碍与否的状态。对于前一半障碍点的每一种状态,DP 出从 0 号点走到每一个点有奇数条还是偶数条路径(记为 $f$),将所有后一半障碍点的 DP 值压成二进制后统计每个二进制数的数量。对于后一半障碍点的每一种状态,DP 出从每个点走到 1 号点有奇数还是偶数条路径(记为 $g$),也是将所有后一半障碍点的 DP 值压成二进制,记录。

现在就要中途相遇合并了,利用我们已经记录的信息,可以求出路径上第一个遇见的障碍点是 $k$的时候,路径有偶数还是奇数条(即 $(f_k * g_k) \bmod{2}$)。发现模 2 其实就是个与运算,用 FWT 合并信息即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define RI register int
typedef long long LL;
int n,tot,js,top,tim;LL ans;
int h[53],ne[505],to[505],du[53],st[53],p[53],pos[53];
int barrier[34],f[53],id_barr[53],sz[131080];
LL lcnt[131080],rcnt[131080];

#define bin(x) (1<<(x))
bool cmp(int x,int y) {return pos[x]<pos[y];}
void add(int x,int y) {to[++tot]=y,ne[tot]=h[x],h[x]=tot;}
void toposort() {
    for(RI i=1;i<=n;++i)
        for(RI j=h[i];j;j=ne[j]) ++du[to[j]];
    for(RI i=1;i<=n;++i) if(!du[i]) st[++top]=i;
    while(top) {
        int x=st[top--];p[++tim]=x,pos[x]=tim;
        for(RI i=h[x];i;i=ne[i]) {
            --du[to[i]];
            if(!du[to[i]]) st[++top]=to[i];
        }
    }
    sort(barrier+1,barrier+1+js,cmp);
}
void work_l() {
    for(RI zt=0;zt<bin(js/2);++zt) {
        for(RI i=1;i<=n;++i) f[i]=0;
        f[1]=1;
        for(RI i=1;i<=n;++i) {
            int x=p[i];
            if(id_barr[x]&&id_barr[x]<=js/2&&(zt&bin(id_barr[x]-1))) f[x]=0;
            if(f[x]==0||id_barr[x]>js/2) continue;
            for(RI j=h[x];j;j=ne[j]) f[to[j]]^=f[x];
        }
        int kzt=0;
        for(RI i=js/2+1;i<=js;++i)
            if(f[barrier[i]]) kzt|=bin(i-js/2);
        if(f[2]) kzt|=1;
        ++lcnt[kzt];
    }
}
void work_r() {
    for(RI zt=0;zt<bin(js-js/2);++zt) {
        for(RI i=1;i<=n;++i) f[i]=0;
        f[2]=1;
        for(RI i=n;i>=1;--i) {
            int x=p[i];
            if(id_barr[x]>js/2&&(zt&bin(id_barr[x]-js/2-1))) {f[x]=0;continue;}
            for(RI j=h[x];j;j=ne[j]) f[x]^=f[to[j]];
        }
        int kzt=0;
        for(RI i=js/2+1;i<=js;++i)
            if(f[barrier[i]]) kzt|=bin(i-js/2);
        if(f[2]) kzt|=1;
        ++rcnt[kzt];
    }
}

void FWT(LL *a,int n,LL x) {
    for(RI i=1;i<n;i<<=1)
        for(RI j=0;j<n;j+=(i<<1))
            for(RI k=0;k<i;++k) a[j+k]+=x*a[j+i+k];
}
class EvenPaths{
    public:
    LL theCount(vector<string> mp,string rooms) {
        n=mp.size();
        for(RI i=0;i<n;++i)
            for(RI j=0;j<n;++j)
                if(mp[i][j]=='Y') add(i+1,j+1);
        for(RI i=0;i<n;++i)
            if(rooms[i]=='?') barrier[++js]=i+1;
        toposort();
        for(RI i=1;i<=js;++i) id_barr[barrier[i]]=i;
        int len=bin(js-js/2+1);
        work_l(),work_r();
        FWT(lcnt,len,1),FWT(rcnt,len,1);
        for(RI i=0;i<len;++i) lcnt[i]*=rcnt[i];
        FWT(lcnt,len,-1);
        for(RI i=1;i<len;++i) sz[i]=sz[i>>1]+(i&1);
        for(RI i=0;i<len;++i) if(!(sz[i]&1)) ans+=lcnt[i];
        return ans;
    }
};

EvenPaths nico;
int main()
{
    string rooms,ks;vector<string> mp;
    int n;scanf("%d",&n);
    cin>>rooms;
    for(RI i=1;i<=n;++i) cin>>ks,mp.push_back(ks);
    cout<<nico.theCount(mp,rooms)<<endl;
    return 0;
}
分类: 文章

litble

苟...苟活者在淡红的血色中,会依稀看见微茫的希望

发表评论

电子邮件地址不会被公开。 必填项已用*标注