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{\n"); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;\n",i,vb[e]); puts("}");}
#define VIZ2 {printf("graph G{\n"); for(int i=1;i<=n;i++) for es(i,e) if(vb[e]>=i)printf("%d--%d;\n",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]<<"\n";
}

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{\n"); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;\n",i,vb[e]); puts("}");}
#define VIZ2 {printf("graph G{\n"); for(int i=1;i<=n;i++) for es(i,e) if(vb[e]>=i)printf("%d--%d;\n",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<<"\r";
    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<<"\n";
}

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

画个图好了...

我们发现这是一张十分美观的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{\n"); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;\n",i,vb[e]); puts("}");}
#define VIZ2 {printf("graph G{\n"); for(int i=1;i<=n;i++) for es(i,e) if(vb[e]>=i)printf("%d--%d;\n",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<<"!!\n"; 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<<"\n";
}

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{\n"); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;\n",i,vb[e]); puts("}");}
#define VIZ2 {printf("graph G{\n"); for(int i=1;i<=n;i++) for es(i,e) if(vb[e]>=i)printf("%d--%d;\n",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\n";
        break;
    }
    if(qw>=minn) continue; minn=qw;
    for(int i=0;i<n;i++) to[i]=od[i];
    cerr<<qw<<"\n";
    }
    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()<<"\n";
        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\n",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{\n"); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;\n",i,vb[e]); puts("}");}
#define VIZ2 {printf("graph G{\n"); for(int i=1;i<=n;i++) for es(i,e) if(vb[e]>=i)printf("%d--%d;\n",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<<"\r";
    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\n";
    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\n";
    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\n";
    dp(g);
    ll p=((F[g][0][K]+G[g][0][K])%P+P)%P;
    cout<<"\n\n\n\n"<<p<<"\n";
}

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{\n"); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;\n",i,vb[e]); puts("}");}
#define VIZ2 {printf("graph G{\n"); for(int i=1;i<=n;i++) for es(i,e) if(vb[e]>=i)printf("%d--%d;\n",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<<"\n";
    for(int i=1;i<=m;i++) cout<<od[ea[i]]<<" "<<od[eb[i]]<<"\n";
}

重编号之后就可以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{\n"); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;\n",i,vb[e]); puts("}");}
#define VIZ2 {printf("graph G{\n"); for(int i=1;i<=n;i++) for es(i,e) if(vb[e]>=i)printf("%d--%d;\n",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\n";
    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\n";
    }
    cerr<<"\n";
    ll ans=0;
    for(int j=0;j<goo[n&1].size();j++)
        ans+=f[n&1][j][K];
    ans%=p; cout<<ans<<"\n";
}

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

时间: 2024-10-13 10:45:10

uoj259 & 独立集问题的一些做法的相关文章

hdu 1569 方格取数(2) 网络流 最大点权独立集

方格取数(2) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 5146    Accepted Submission(s): 1610 Problem Description 给你一个m*n的格子的棋盘,每个格子里面有一个非负数. 从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的

【BZOJ-1952】Area [坑题] 仙人掌DP + 最大点权独立集(改)

1952: [Sdoi2010]Area Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 73  Solved: 23[Submit][Status][Discuss] Description 小猪iPig来到了一个叫做pigsty的城市里,pigsty是一座专门为小猪所准备的城市,城市里面一共有n个小区给小猪们居住,并且存在许多条无向边连接着许多小区.因为这里是一个和谐的城市,所以小猪iPig准备在这个城市里面度过他的余生.若干年之后小猪iPig

hdoj 1569 方格取数 【最大点权独立集-&gt;最大流】

题目:hdoj 1569 方格取数 题意:中文题目,就不说题意了. 分类:最大流 |  dp 分析:dp的话应该是个数塔模型,不难做,这里讲转化为图的做法. 这个题目的关键在于转化为一个二分图,来求一个二分图的最大点权独立集,而最大点权独立集 = 点权和 - 最小点权覆盖 最小点权覆盖: 从x或者y集合中选取一些点,使这些点覆盖所有的边,并且选出来的点的权值尽可能小. 最大点权独立集:找到二分图中权值和最大的点集,然后让任意点没有边. 而最小点权覆盖 = 最小割 = 最大流 = sum - 最大

几个解决k染色问题的指数级做法

几个解决k染色问题的指数级做法 ——以及CF908H题解 给你一张n个点的普通无向图,让你给每个点染上k种颜色中的一种,要求对于每条边,两个端点的颜色不能相同,问你是否存在一种可行方案,或是让你输出一种可行方案,或是让你求出满足条件的最小的k.这种问题叫做k染色问题.众所周知,当k>2时,k染色问题是NP的.但是相比$O(k^n)$的暴力搜索来说,人们还是找到了很多复杂度比较优越的指数级做法.本文简单介绍其中几种. 因为对于$O(n^22^n)$来说,$O(n^2)$小得可以忽略不计,所以在本文

hdu 1565 方格取数(2)(网络流之最大点权独立集)

题目链接:hdu 1565 方格取数(2) 题意: 有一个n*m的方格,每个方格有一个数,现在让你选一些数.使得和最大. 选的数不能有相邻的. 题解: 我们知道对于普通二分图来说,最大独立点集 + 最小点覆盖集 = 总点数,类似的,对于有权的二分图来说,有: 最大点权独立集 + 最小点权覆盖集 = 总点权和, 这个题很明显是要求 最大点权独立集 ,现在 总点权 已知,我们只要求出来 最小点权覆盖集 就好了,我们可以这样建图, 1,对矩阵中的点进行黑白着色(相邻的点颜色不同),从源点向黑色的点连一

【POJ3659】【USACO 2008 Jan Gold】 3.Cell Phone Network 树上最小支配集/贪心 两种做法

题意:求树上最小支配集 最小支配集:点集,即每个点可以"支配"到相邻点,求最少点数可以使所有点被支配. 图上的最小支配集是NP的,但是树上的可以DP做,是O(n)的. 暴力做就好了, f[i]表示此 点被选时的子树全支配的最小代价 g[i]表示其父亲节 点被选时的子树全支配的最小代价 h[i]表示其某子节 点被选时的子树全支配的最小代价 然后暴力转移. (v是子节点) f[x]=∑(min(f[v],min(g[v],h[v])))+1; g[x]=∑(min(f[v],h[v]));

HDU 1569 - 方格取数(2) - [最大点权独立集与最小点权覆盖集]

嗯,这是关于最大点权独立集与最小点权覆盖集的姿势,很简单对吧,然后开始看题. HDU1569: Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Problem Description 给你一个m*n的格子的棋盘,每个格子里面有一个非负数.从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大. Input 包括多个测试实例,

编写Dockerfiles的最佳做法

编写Dockerfiles的最佳做法 Docker可以通过从Dockerfile包含所有命令的文本文件中读取 指令,自动构建图像,以便构建给定图像所需的顺序.Dockerfile坚持一个具体的格式,并使用一组具体的说明.您可以在Dockerfile参考页面上了解基础知识 .如果你是新来Dockerfile的,你应该从那里开始. 本文档介绍了Docker,Inc.和Docker社区推荐的最佳做法和方法,以创建易于使用,有效 Dockerfile的.我们强烈建议您遵循这些建议(实际上,如果您正在创建

HDU1569 最大流(最大点权独立集)

方格取数(2) Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 68 Accepted Submission(s): 33   Problem Description 给你一个m*n的格子的棋盘,每个格子里面有一个非负数.从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大. In