zoukankan      html  css  js  c++  java
  • uoj259 & 独立集问题的一些做法

    很早以前就做了一遍这题,当时好像啥都不会,今天重做一下。

    这个题题意简单地说就是输入k、p和一个图,求图大小为k的独立集个数mod p。

    subset1.in  n=24,m=19,k=8

    subset2.in  n=40,m=55,k=11

    subset3.in  n=100,m=99,k=32

    n比较小,直接状压dp即可,细节详见集训队论文(懒得写了),跑得飞快。

    #include <iostream>
    #include <stdio.h>
    #include <math.h>
    #include <string.h>
    #include <time.h>
    #include <stdlib.h>
    #include <string>
    #include <bitset>
    #include <vector>
    #include <set>
    #include <map>
    #include <queue>
    #include <algorithm>
    #include <sstream>
    #include <stack>
    #include <iomanip>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    typedef pair<int,int> pii;
    typedef long long ll;
    typedef double ld;
    typedef vector<int> vi;
    #define fi first
    #define se second
    #define fe first
    #define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
    #define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
    #define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
    #define es(x,e) (int e=fst[x];e;e=nxt[e])
    #define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
    #define VIZ {printf("digraph G{
    "); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;
    ",i,vb[e]); puts("}");}
    #define VIZ2 {printf("graph G{
    "); for(int i=1;i<=n;i++) for es(i,e) if(vb[e]>=i)printf("%d--%d;
    ",i,vb[e]); puts("}");}
    #define SZ 666666
    int n,m,k,p,cnt=0;
    #include <unordered_map>
    struct cn
    {
    ll f[33];
    cn() {memset(f,0,sizeof f);}
    ll& operator [] (unsigned p) {return f[p];}
    };
    cn operator + (cn a,cn b)
    {
        for(int i=1;i<=k;i++)
            a[i]=(a[i]+b[i])%p;
        return a;
    }
    cn operator * (cn a,cn b)
    {
        cn t=a+b;
        for(int i=1;i<=k;i++)
            for(int j=1;j<=k;j++)
                if(i+j<=k)
                    t[i+j]=(t[i+j]+a[i]*b[j])%p;
        return t;
    }
    cn add1(cn a)
    {
        cn t; t[1]=1;
        for(int i=1;i<k;i++)
            t[i+1]=a[i];
        return t;
    }
    unordered_map<bitset<203>,cn> F;
    bitset<203> goo[233];
    int d[233],ts[233],ff[233];
    inline int gf(int x) {return ff[x]?ff[x]=gf(ff[x]):x;}
    cn f(bitset<203> r)
    {
        if(F.count(r)) return F[r];
        int p=r.count();
        if(!p) return F[r]=cn();
        int tn=0,cp=0; cn&g=F[r];
        for(int i=1;i<=n;i++)
            if(r[i]) ts[++tn]=i,ff[tn]=0,d[tn]=3;
        for(int i=1;i<=tn;i++)
            for(int j=1;j<=tn;j++)
            {
                if(goo[ts[i]][ts[j]]) continue;
                ++d[i]; int ga=gf(i),gb=gf(j);
                if(ga!=gb) ff[ga]=gb;
            }
        for(int i=1;i<=tn;i++)
            if(gf(i)==i) ++cp;
        if(cp>1)
        {
            bitset<203>*rs=(bitset<203>*)malloc(sizeof(bitset<203>)*(tn+2));
            for(int i=1;i<=tn;i++) rs[i].reset();
            for(int i=1;i<=tn;i++)
                rs[gf(i)][ts[i]]=1;
            cn ans;
            for(int i=1;i<=tn;i++)
                if(rs[i].count()) ans=ans*f(rs[i]);
            return g=ans;
        }
        int s=1;
        for(int i=1;i<=tn;i++) if(d[i]>d[s]) s=i;
        s=ts[s]; r[s]=0; cn f1=f(r);
        r&=goo[s]; cn f2=add1(f(r));
        return g=f1+f2;
    }
    int main()
    {
        scanf("%d%d%d%d",&n,&m,&k,&p);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++) goo[i][j]=1;
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            goo[a][b]=goo[b][a]=0;
        }
        bitset<203> all;
        for(int i=1;i<=n;i++) all[i]=1;
        cn ans=f(all); cout<<ans[k]<<"
    ";
    }

    subset4.in  n=100000,m=99999,k=20358

    100000 99999 20358 999999937
    1 2
    1 3
    2 4
    1 5
    4 6
    6 7
    6 8
    6 9
    2 10
    8 11
    4 12
    2 13
    8 14
    10 15
    11 16
    10 17
    ...

    树?树上大小为k的独立集数量?直接树形dp大概就行了。

    记f[i][0/1][j]为包含/不包含i,i子树中,大小为j的独立集数量。暴力转移复杂度大概是平方。

    跑了好久好久跑出来了...可能有5min。如果把暴力卷积改成fft(由于数据随机)应该会快一点。

    #include <iostream>
    #include <stdio.h>
    #include <math.h>
    #include <string.h>
    #include <time.h>
    #include <stdlib.h>
    #include <string>
    #include <bitset>
    #include <vector>
    #include <set>
    #include <map>
    #include <queue>
    #include <algorithm>
    #include <sstream>
    #include <stack>
    #include <iomanip>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    typedef pair<int,int> pii;
    typedef long long ll;
    typedef double ld;
    typedef vector<int> vi;
    #define fi first
    #define se second
    #define fe first
    #define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
    #define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
    #define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
    #define es(x,e) (int e=fst[x];e;e=nxt[e])
    #define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
    #define VIZ {printf("digraph G{
    "); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;
    ",i,vb[e]); puts("}");}
    #define VIZ2 {printf("graph G{
    "); for(int i=1;i<=n;i++) for es(i,e) if(vb[e]>=i)printf("%d--%d;
    ",i,vb[e]); puts("}");}
    #define SZ 666666
    int n,m,k,P,sz[SZ],cnt=0; Edg
    vector<int> vs[100003][2];
    void dfs(int x)
    {
        cerr<<++cnt<<"
    ";
        vs[x][0].resize(2);
        vs[x][1].resize(2);
        int cs=1; vs[x][0][0]=vs[x][1][1]=1;
        for esb(x,e,g)
        {
            dfs(g);
            {
                vector<int> tmp,&v1=vs[x][1],&v2=vs[g][0];
                tmp.resize(cs+sz[g]+1);
                for(int a=0;a<=cs;a++)
                    for(int b=0;b<=sz[g];b++)
                        tmp[a+b]=(tmp[a+b]+(ll)v1[a]*v2[b])%P;
                v1=tmp;
            }
            {
                vector<int> tmp,&v1=vs[x][0],&v2=vs[g][0],&v3=vs[g][1];
                tmp.resize(cs+sz[g]+1);
                for(int a=0;a<=cs;a++)
                    for(int b=0;b<=sz[g];b++)
                        tmp[a+b]=(tmp[a+b]+(ll)v1[a]*(v2[b]+v3[b]))%P;
                v1=tmp; v2.clear(); v3.clear();
            }
            cs+=sz[g];
        }
        sz[x]=cs;
    }
    int main()
    {
        scanf("%d%d%d%d",&n,&m,&k,&P);
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            ad_de(a,b);
        }
        dfs(1);
        ll ans=vs[1][0][k]+vs[1][1][k];
        ans=(ans%P+P)%P; cout<<ans<<"
    ";
    }

    subset5.in n=144,m=264,k=20

    看起来奥妙重重。

    144 264 20 998244353
    1 2
    1 13
    2 3
    2 14
    3 4
    3 15
    4 5
    4 16
    5 6
    5 17
    6 7
    6 18
    7 8
    7 19
    8 9
    8 20
    9 10
    9 21
    10 11

    画个图好了...

    image

    我们发现这是一张十分美观的12*12的网格图,大力状压dp一波好了。

    我们直接用f[i][j][k]表示前i行,第i行选的独立集状压之后是j,前i行一共选的大小为k,直接dp即可。

    #include <iostream>
    #include <stdio.h>
    #include <math.h>
    #include <string.h>
    #include <time.h>
    #include <stdlib.h>
    #include <string>
    #include <bitset>
    #include <vector>
    #include <set>
    #include <map>
    #include <queue>
    #include <algorithm>
    #include <sstream>
    #include <stack>
    #include <iomanip>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    typedef pair<int,int> pii;
    typedef long long ll;
    typedef double ld;
    typedef vector<int> vi;
    #define fi first
    #define se second
    #define fe first
    #define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
    #define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
    #define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
    #define es(x,e) (int e=fst[x];e;e=nxt[e])
    #define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
    #define VIZ {printf("digraph G{
    "); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;
    ",i,vb[e]); puts("}");}
    #define VIZ2 {printf("graph G{
    "); for(int i=1;i<=n;i++) for es(i,e) if(vb[e]>=i)printf("%d--%d;
    ",i,vb[e]); puts("}");}
    #define SZ 666666
    int K,P,c1[SZ];
    bool valid[SZ];
    int f[2][4444][22];
    int main()
    {
        scanf("%*d%*d%d%d",&K,&P);
        int n=12;
        for(int i=0;i<(1<<n);i++)
        {
            valid[i]=1;
            for(int j=0;j+1<n;j++)
            {
                if((i&(1<<j))&&(i&(1<<(j+1))))
                    valid[i]=0;
            }
            for(int j=0;j<n;j++) c1[i]+=bool(i&(1<<j));
        }
        f[0][0][0]=1;
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            cerr<<i<<"!!
    "; ans=0;
            memset(f[i&1],0,sizeof f[0]);
            auto &c=f[i&1],&p=f[(i&1)^1];
            for(int j=0;j<(1<<n);j++)
            {
                if(!valid[j]) continue;
                for(int k=0;k<(1<<n);k++)
                {
                    if(!valid[k]||(j&k)) continue;
                    for(int g=c1[j];g<=K;g++)
                        c[j][g]=(c[j][g]+p[k][g-c1[j]])%P;
                }
                ans=(ans+c[j][K])%P;
            }
        }
        cout<<ans<<"
    ";
    }

    subset6.in n=500,m=5000,k=55

    看起来非常奥妙重重,但是这个图有点大,我并不能画出来。

    我猜这是一个k仙人图!测了测,一条树边至多被...2098条边覆盖?好僵硬啊

    看了看度数,感觉非常正态分布?这个点不会是随机的吧...

    既然这个点能做,那么肯定有特殊性质...我猜这是一个分层图!

    那么问题来了,怎么把一个分层图分层...

    一种简单的做法是bfs这个图,那么同一层的bfs序上比较容易挨在一起。

    我们定义od[i]为i的新编号,同一层的编号挨在一起。

    假设每层点数一样,因为一条边不是层内的就是跨层的,所以每层点数只要定为max(|od[a]-od[b]|)(a、b是某条边的端点)上取整即可。

    按输入顺序连完边好像max(|od[a]-od[b]|)大概是40,太大了。多shuffle几次边跑了一会儿跑出来一个max(|od[a]-od[b]|)=30的解,那么每层可以30个点。

    我们还是用f[i][j][k]表示前i层,第i行选的独立集状压之后是j,前i行一共选的大小为k。

    直接dp复杂度似乎复杂度妥妥爆炸,但是因为这个图比较密,所以每一层合法的独立集没有这么多,所以实际上还是能跑出来的,跑得还挺快的。

    调了一年:

    #include <iostream>
    #include <stdio.h>
    #include <math.h>
    #include <string.h>
    #include <time.h>
    #include <stdlib.h>
    #include <string>
    #include <bitset>
    #include <vector>
    #include <set>
    #include <map>
    #include <queue>
    #include <algorithm>
    #include <sstream>
    #include <stack>
    #include <iomanip>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    typedef pair<int,int> pii;
    typedef long long ll;
    typedef double ld;
    #define fi first
    #define se second
    #define fe first
    #define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
    #define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
    #define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
    #define es(x,e) (int e=fst[x];e;e=nxt[e])
    #define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
    #define VIZ {printf("digraph G{
    "); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;
    ",i,vb[e]); puts("}");}
    #define VIZ2 {printf("graph G{
    "); for(int i=1;i<=n;i++) for es(i,e) if(vb[e]>=i)printf("%d--%d;
    ",i,vb[e]); puts("}");}
    #define SZ 666666
    Edgc
    bool edg[2333][2333];
    int n,m,K,p,ea[SZ],eb[SZ],od[SZ],to[SZ];
    void bfs(int x)
    {
        static int qs[SZ];
        static bool vis[SZ];
        for(int i=0;i<n;i++) vis[i]=0;
        int h=0,t=0,C=0; qs[t++]=x;vis[x]=1;
        while(h^t)
        {
            int x=qs[h++]; od[x]=C++;
            for esb(x,e,b)
            {
                if(vis[b]) continue;
                vis[b]=1; qs[t++]=b;
            }
        }
    }
    int vi[SZ];
    pii ps[SZ];
    vector<int> sta[233];
    ll f[2][65537][56];
    typedef bitset<32> bs;
    vector<int>*tv; int off,nn;
    void dfs(int st,int cur,bs x)
    {
        tv->pb(cur);
        for(int g=x._Find_next(st);g<nn;g=x._Find_next(g))
        {
            bs nx=x;
            for(int j=0;j<nn;j++)
                if(edg[off+g][off+j]) nx[j]=0;
            dfs(g,cur|(1<<g),nx);
        }
    }
    int main()
    {
        srand(19260817); //如何让程序跑得飞快 
        scanf("%d%d%d%d",&n,&m,&K,&p);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&ps[i].fi,&ps[i].se);
            --ps[i].fi,--ps[i].se;
        }
        int minn=1e9;
        while(1)
        {
        for(int i=0;i<n;i++) fst[i]=0; M=0;
        random_shuffle(ps+1,ps+1+m);
        for(int i=1;i<=m;i++)
        {
            ea[i]=ps[i].fi;eb[i]=ps[i].se;
            adde(ea[i],eb[i],i);
        }
        bfs(0); int qw=0;
        for(int i=1;i<=m;i++)
            qw=max(qw,abs(od[eb[i]]-od[ea[i]]));
        if(minn<=31)
        {
            cerr<<"good
    ";
            break;
        }
        if(qw>=minn) continue; minn=qw;
        for(int i=0;i<n;i++) to[i]=od[i];
        cerr<<qw<<"
    ";
        }
        for(int i=0;i<n;i++) od[i]=to[i];
        int P=minn,c=0;
        for(int i=1;i<=m;i++)
            edg[od[ps[i].fi]][od[ps[i].se]]=
            edg[od[ps[i].se]][od[ps[i].fi]]=1;
        for(int i=0;i*P<n;i++)
        {
            ++c; int s=min(n-i*P,P);
            bs p; for(int i=0;i<s;i++) p[i]=1;
            tv=&sta[c]; off=i*P; nn=s; dfs(-1,0,p);
        }
        f[1][0][0]=1; sta[0].pb(0);
        static int x[233],y[233];
        for(int i=0;i<c;i++)
        {
            cerr<<"layer"<<i<<"/"<<c<<" "<<sta[i+1].size()<<"
    ";
            memset(f[i&1],0,sizeof f[0]);
            ll ans=0;
            for(int ij=0;ij<sta[i+1].size();ij++)
            {
                int j=sta[i+1][ij];
                for(int g=0;g<=K;g++)
                    f[i&1][ij][g]=0;
                for(int ik=0;ik<sta[i].size();ik++)
                {
                    int k=sta[i][ik];
                    int xn=0,yn=0;
                    for(int a=0;a<P;a++)
                        if(j&(1<<a)) x[++xn]=a;
                    for(int a=0;a<P;a++)
                        if(k&(1<<a)) y[++yn]=a;
                    for(int _=1;_<=xn;_++)
                    {
                        for(int __=1;__<=yn;__++)
                        {
                            int a=x[_],b=y[__];
                            if(edg[a+i*P][b+i*P-P])
                                goto gg2;
                        }
                    }
                    //cont. xn
                    for(int g=xn;g<=K;g++)
                        f[i&1][ij][g]=(f[i&1][ij][g]+f[!(i&1)][ik][g-xn])%p;
                    gg2:;
                }
                ans=(ans+f[i&1][ij][K])%p;
            }
            if(i==c-1) printf("%lld
    ",ans);
        }
    }

    subset7.in n=4998,m=5002,k=666

    subset8.in n=11986,m=12011,k=1098

    这两个点的共同点是m≈n,m-n不大,连通图。

    这种图可以通过dfs树+覆盖边状压dp解决,就状压覆盖父亲边的非树边的顶端的选择情况。

    细节不清楚的看集训队论文吧...写起来着实感人。

    #include <iostream>
    #include <stdio.h>
    #include <math.h>
    #include <string.h>
    #include <time.h>
    #include <stdlib.h>
    #include <string>
    #include <bitset>
    #include <vector>
    #include <set>
    #include <map>
    #include <queue>
    #include <algorithm>
    #include <sstream>
    #include <stack>
    #include <iomanip>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    typedef pair<int,int> pii;
    typedef long long ll;
    typedef double ld;
    typedef vector<int> vi;
    #define fi first
    #define se second
    #define fe first
    #define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
    #define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
    #define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
    #define es(x,e) (int e=fst[x];e;e=nxt[e])
    #define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
    #define VIZ {printf("digraph G{
    "); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;
    ",i,vb[e]); puts("}");}
    #define VIZ2 {printf("graph G{
    "); for(int i=1;i<=n;i++) for es(i,e) if(vb[e]>=i)printf("%d--%d;
    ",i,vb[e]); puts("}");}
    #define SZ 666666
    Edg
    int n,m,dfn[SZ],C=0,dep[SZ],fa[SZ],fc[SZ];
    bool vis[SZ];
    void dfs(int x)
    {
        dfn[x]=++C; vis[x]=1;
        for esb(x,e,b)
            if(!vis[b]) dep[b]=dep[x]+1,fa[b]=x,dfs(b);
    }
    int ea[SZ],eb[SZ],K,P,ys[23333333];bool te[SZ];
    vi cv[SZ];
    //use array ys to for binary ones in x.
    #define forb(x,y) for(int x##__temp__=x,y;x##__temp__&&(y=ys[x##__temp__&(-x##__temp__)])+1;x##__temp__-=1<<y)
    //-Wl,--stack=23333333 might be needed.
    struct Arr{int g[1100];
    Arr() {for(int i=0;i<=K;i++) g[i]=0;}
    inline int& operator[] (int x) {return g[x];}
    const Arr& operator = (const Arr&b)
    {for(int i=0;i<=K;i++) g[i]=b.g[i];}};
    vector<Arr> F[12000],G[12000];
    Arr operator + (Arr a,Arr b)
    {
        Arr p;
        for(int i=0;i<=K;i++) p[i]=(a[i]+b[i])%P;
        return p; 
    }
    Arr operator * (Arr a,Arr b)
    {
        Arr p;
        for(int i=0;i<=K;i++)
        {
            if(!a[i]) continue;
            for(int j=0;i+j<=K;j++)
            {
                if(!b[j]) continue;
                p[i+j]=(p[i+j]+(ll)a[i]*b[j])%P;
            }
        }
        return p;
    }
    bool ins[SZ];
    int cnt=0,tot=0;
    void dp(int x)
    {
        vector<int> son;
        for esb(x,e,b)
        {
            if(fa[b]!=x) continue;
            dp(b); son.pb(b);
        }
        vector<Arr> lf,cf,lg,cg,em;
        vector<bool> valg;
        int S=1<<cv[x].size();
        cout<<(cnt+=S)/(double)tot<<"
    ";
        valg.resize(S);
        for(int j=0;j<S;j++)
        {
            forb(j,g)
                if(eb[cv[x][g]]==x) goto not_valid_QAQ;
            valg[j]=1; not_valid_QAQ:;
        }
        em.resize(S); lf=cf=lg=cg=em;
        for(int j=0;j<(1<<cv[x].size());j++)
        {
            cf[j][0]=1;
            if(valg[j]) cg[j][1]=1;
        }
        for(auto s:son)
        {
            lf=cf; lg=cg; cf=cg=em;
            //cons. f
            for(int j=0;j<(1<<cv[x].size());j++)
            {
                forb(j,g) ins[ea[cv[x][g]]]=1;
                {
                int su=0;
                for(int k=0;k<cv[s].size();k++)
                    if(ins[ea[cv[s][k]]]) su|=1<<k;
                cf[j]=lf[j]*(F[s][su]+G[s][su]);
                }
                if(valg[j])
                {
                ins[x]=1;
                int su=0;
                for(int k=0;k<cv[s].size();k++)
                    if(ins[ea[cv[s][k]]]) su|=1<<k;
                cg[j]=lg[j]*F[s][su];
                ins[x]=0;
                }
                forb(j,g) ins[ea[cv[x][g]]]=0;
            }
            F[s].clear(); G[s].clear();
        }
        F[x]=cf; G[x]=cg;
    }
    int main()
    {
        for(int i=0;(1<<i)<23333333;++i) ys[1<<i]=i;
        srand(19260817);
        scanf("%d%d%d%d",&n,&m,&K,&P);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",ea+i,eb+i),
            adde(ea[i],eb[i]);
            if(ea[i]==eb[i]) throw "WTF";
        }
        cerr<<"Finding Root
    ";
        ll minn=1e18; int g;
        for(int p=1;p<=n;p++)
        {
        C=0;
        for(int i=1;i<=n;i++) fc[i]=vis[i]=fa[i]=0;
        dep[p]=1; dfs(p);
        for(int i=1;i<=m;i++)
        {
            if(fa[ea[i]]==eb[i]||fa[eb[i]]==ea[i]);
            else
            {
                int a=ea[i],b=eb[i];
                if(dep[a]>dep[b]) swap(a,b);
                //a is an ancestor of b
                for(int j=b;j!=a;j=fa[j]) ++fc[j];
            }
        }
        ll s=0;
        for(int i=1;i<=n;i++) s+=1LL<<fc[i];
        if(s<minn) minn=s,g=p;
        }
        cerr<<"PRE2
    ";
        C=0; tot=minn;
        for(int i=1;i<=n;i++) fc[i]=vis[i]=fa[i]=0;
        dep[g]=1; dfs(g);
        for(int i=1;i<=m;i++)
        {
            if(dep[ea[i]]>dep[eb[i]]) swap(ea[i],eb[i]);
            //ea[i] is lower than eb[i]
            if(fa[eb[i]]==ea[i]) te[i]=1;
            else
            {
                int a=ea[i],b=eb[i];
                for(int j=b;j!=a;j=fa[j]) cv[j].pb(i);
            }
        }
        cerr<<"READY
    ";
        dp(g);
        ll p=((F[g][0][K]+G[g][0][K])%P+P)%P;
        cout<<"
    
    
    
    "<<p<<"
    ";
    }

    subset9.in n=32748,m=196467,k=3360。

    1 2
    1 3
    1 4
    5 1
    1 6
    7 1
    ...
    14369 14372
    14373 14369
    14369 14374
    14369 14375
    14370 14371
    14372 14370
    14373 14370
    14374 14370
    14370 14375
    14370 14376
    ...

    这个点的特点是连边的两端点编号差值都不大,直接状压dp即可。

    subset10.in n=64319,m=200000,k=10373。

    这个点看起来非常随机!看起来十分感人。

    我们来统计一下每个点的度数...怎么全在8以内?还有这种操作?

    咦这个点放在9号点后面肯定别有用心,我猜这个点重编号之后连边的两端点编号差值也都不大!

    那么我们现在就是要把点重编号,使得图的bandwidth尽量小。

    求尽量小的bandwidth有一个Cuthill-Mckee Algorithm,算法大致如下:

    这是一种广义的bfs,我们一开始先选定最小度数的点作为起点,给起点标1号,然后每次扩展一圈(和上次扩展的点相邻的点),然后按度数排序并编号。

    用这个算法求出的bandwidth为16。

    #include <iostream>
    #include <stdio.h>
    #include <math.h>
    #include <string.h>
    #include <time.h>
    #include <stdlib.h>
    #include <string>
    #include <bitset>
    #include <vector>
    #include <set>
    #include <map>
    #include <queue>
    #include <algorithm>
    #include <sstream>
    #include <stack>
    #include <iomanip>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    typedef pair<int,int> pii;
    typedef long long ll;
    typedef double ld;
    typedef vector<int> vi;
    #define fi first
    #define se second
    #define fe first
    #define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
    #define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
    #define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
    #define es(x,e) (int e=fst[x];e;e=nxt[e])
    #define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
    #define VIZ {printf("digraph G{
    "); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;
    ",i,vb[e]); puts("}");}
    #define VIZ2 {printf("graph G{
    "); for(int i=1;i<=n;i++) for es(i,e) if(vb[e]>=i)printf("%d--%d;
    ",i,vb[e]); puts("}");}
    #define SZ 666666
    //Try Cuthill-McKee Algorithm
    Edg
    bool vis[SZ];
    int n,m,k,P,d[SZ],od[SZ],ea[SZ],eb[SZ];
    vector<vi> r;
    bool by_d(int a,int b) {return d[a]<d[b];}
    int f[2][129][3456];
    int main()
    {
        scanf("%d%d%d%d",&n,&m,&k,&P);
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            adde(a,b);
            ++d[a];++d[b];
            ea[i]=a,eb[i]=b;
        }
        d[0]=1e9; int md=0;
        for(int i=1;i<=n;i++)
            if(d[i]<d[md]) md=i;
        for(int i=1;i<=n;i++)
            vis[i]=0;
        vis[md]=1; vi tmp; tmp.pb(md);
        r.pb(tmp); int s=1;
        while(s<n)
        {
            vi t=r[r.size()-1],tmp;
            for(auto x:t)
                for esb(x,e,b)
                    if(!vis[b]) tmp.pb(b),vis[b]=1,++s;
            sort(tmp.begin(),tmp.end(),by_d);
            r.pb(tmp);
        }
        int C=0,mx=0;
        for(auto s:r)
            for(auto p:s)
                od[p]=++C;
        cout<<n<<" "<<m<<" "<<k<<" "<<P<<"
    ";
        for(int i=1;i<=m;i++) cout<<od[ea[i]]<<" "<<od[eb[i]]<<"
    ";
    }

    重编号之后就可以dp了,直接dp比较慢,需要注意的是我们可以只存、只dp合法状态,就可以加速了。经过一些wys,大概要跑20min左右。

    #include <iostream>
    #include <stdio.h>
    #include <math.h>
    #include <string.h>
    #include <time.h>
    #include <stdlib.h>
    #include <string>
    #include <bitset>
    #include <vector>
    #include <set>
    #include <map>
    #include <queue>
    #include <algorithm>
    #include <sstream>
    #include <stack>
    #include <iomanip>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    typedef pair<int,int> pii;
    typedef long long ll;
    typedef double ld;
    typedef vector<int> vi;
    #define fi first
    #define se second
    #define fe first
    #define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
    #define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
    #define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
    #define es(x,e) (int e=fst[x];e;e=nxt[e])
    #define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
    #define VIZ {printf("digraph G{
    "); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;
    ",i,vb[e]); puts("}");}
    #define VIZ2 {printf("graph G{
    "); for(int i=1;i<=n;i++) for es(i,e) if(vb[e]>=i)printf("%d--%d;
    ",i,vb[e]); puts("}");}
    #define SZ 666666
    int n,m,fb[SZ],K,p;
    int f[2][5555][11000],bk[131088];
    vector<int> goo[12345];
    int main()
    {
        scanf("%d%d%d%d",&n,&m,&K,&p);
        int S=0,mx=0;
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            if(a>b) swap(a,b);
            //a<=b
            fb[b]|=1<<(b-a);
            S=max(S,1<<(b-a+1));
        }
        cerr<<S<<" "<<K<<"GG
    ";
        f[0][0][0]=1; goo[0].pb(0);
        int pv=0,st=clock();
        for(int i=1;i<=n;i++)
        {
            vi&ti=goo[i&1],&pi=goo[!(i&1)];
            ti.clear();
            for(int _=0;_<pi.size();_++)
            {
                int j=pi[_];
                for(int k=0;k<=K;k++)
                    if(f[!(i&1)][_][k]) goto good_;
                continue;
                good_:
                ti.pb((j<<1)&(S-1));
                if((j<<1)&fb[i]);else
                    ti.pb(((j<<1)&(S-1))|1);
            }
            sort(ti.begin(),ti.end());
            ti.erase(unique(ti.begin(),ti.end()),ti.end());
            mx=max(mx,(int)ti.size());
            for(int j=0;j<ti.size();j++)
                memset(f[i&1][j],0,sizeof f[0][0]),bk[ti[j]]=j;
            for(int _=0;_<pi.size();_++)
            {
                int j=pi[_];
                int*tg=f[i&1][bk[(j<<1)&(S-1)]],
                *ss=f[!(i&1)][_];
                for(int k=0;k<=K;k+=8)
                {
                    #define par(s) 
                    tg[k+s]+=ss[k+s]; if(tg[k+s]>=p) tg[k+s]-=p;
                    par(0) par(1) par(2) par(3)
                    par(4) par(5) par(6) par(7)
                    #undef par
                }
                if((j<<1)&fb[i]) continue;
                int*t2=f[i&1][bk[((j<<1)&(S-1))|1]]+1;
                for(int k=0;k<=K;k+=8)
                {
                    #define par(s) 
                    t2[k+s]+=ss[k+s]; if(t2[k+s]>=p) t2[k+s]-=p;
                    par(0) par(1) par(2) par(3)
                    par(4) par(5) par(6) par(7)
                    #undef par
                }
            }
            if(clock()-pv>1000) pv=clock(),
                cerr<<i<<" "<<goo[i&1].size()<<" "<<S<<" "<<mx<<"w"<<(n-i)/(ld)i*(clock()-st)/1000.0<<"s rm
    "; 
        }
        cerr<<"
    ";
        ll ans=0;
        for(int j=0;j<goo[n&1].size();j++)
            ans+=f[n&1][j][K];
        ans%=p; cout<<ans<<"
    ";
    }

    这个题好像做了好久啊,完结撒花

  • 相关阅读:
    re | [SWPU2019]ReverseMe
    wp | re | 2020“巅峰极客”网络安全技能挑战赛
    re | [CFI-CTF 2018]IntroToPE
    re | [FlareOn1]Bob Doge
    re | [ACTF新生赛2020]SoulLike
    re | [GKCTF2020]Chelly's identity
    ospf配置与ofps选举DR/BDR
    静态路由的配置
    配置三层交换机实现vlan间通信
    hybrid接口
  • 原文地址:https://www.cnblogs.com/zzqsblog/p/6937081.html
Copyright © 2011-2022 走看看