BZOJ1494:[NOI2007]生成树计数 矩阵乘法+最小表示法+插头dp

思路:

从前向后考虑每一个点,不难发现每个点只能向它的前\(k\)个点连边.

因此我们维护一下最后\(k\)个点的连通性,然后做状压dp.

我们用最小表示法来表示\(k\)个点的连通性,比如12123这种形式,相同的数字表示在相同集合中.

然后我们就枚举一下第\(k+1\)个点如何向前\(k\)个点连边.

注意:合法的连边不能出现环,同时还要保证第\(1\)个点不能被孤立.

然后就有了一个转移矩阵,就可以跑矩乘了.

注意每个状态的初始方案并不是一,而是每个每个连通图的所有生成树的方案之积.

而由Prufer序列可得\(n\)个点的生成树总数为\(n^{n-2}\).

至此应该没有什么问题了(除了代码实现).

 

#include<map>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
 
long long n;int k;
 
int lim;
 
int cnt,seq[110][10],now[10],vis[10];
 
void dfs(int dep){
    if(dep==k+1){
        memset(vis,0,sizeof vis);
        for(int i=1;i<=k;++i)if(!vis[now[i]])vis[now[i]]=i;
        for(int i=1;i<=lim;++i)if(!vis[i])return;
        for(int i=1;i<=lim;++i)for(int j=i+1;j<=lim;++j)if(vis[i]>vis[j])return;
         
        ++cnt;for(int i=1;i<=k;++i)seq[cnt][i]=now[i];
        return;
    }
     
    for(int i=1;i<=lim;++i)now[dep]=i,dfs(dep+1);
}
 
#define Min(x,y) ((x)<(y)?(x):(x))
 
inline int calc(int d){
    int mi=1,re=0;
    for(int i=1;i<=k;++i)re+=mi*seq[d][i],mi*=10;
    return re;
}
map<int,int>M;
 
static const int mod=65521;
inline void inc(int&x,int y){if((x+=y)>=mod)x-=mod;}
 
struct Matrix{
    int w,h,d[110][110];
    Matrix(int _w,int _h):w(_w),h(_h){memset(d,0,sizeof d);}
     
    inline void operator*=(const Matrix&B){
        Matrix C(w,B.h);
        for(int i=1;i<=C.w;++i)for(int j=1;j<=C.h;++j)
            for(int k=1;k<=h;++k)inc(C.d[i][j],(long long)d[i][k]*B.d[k][j]%mod);
        *this=C;
    }
};
 
int pre[10],pa[10],nowseq[10];
 
inline int find(int p[],int x){return x==p[x]?x:p[x]=find(p,p[x]);}
inline void merge(int p[],int x,int y){p[find(p,x)]=find(p,y);}
 
const int get[]={1,1,1,3,16,125};
 
int main(){
    scanf("%d%lld",&k,&n),k=Min(k,n-1);register int i,j;
     
    for(i=1;i<=k;++i){
        lim=i,dfs(1);
    }
     
    Matrix ans(1,cnt);
    for(i=1;i<=cnt;++i){
        ans.d[1][i]=1;
        for(j=1;j<=k;++j){int count=0;for(int q=1;q<=k;++q)if(seq[i][q]==j)++count;ans.d[1][i]*=get[count];}
         
        M[calc(i)]=i;
    }
     
    Matrix add(cnt,cnt);
     
    for(int t=1;t<=cnt;++t){
        for(i=1;i<=k+1;++i)pre[i]=i;
        for(i=1;i<=k;++i)for(j=i+1;j<=k;++j)if(seq[t][i]==seq[t][j])merge(pre,i,j);
         
        for(int S=0;S<(1<<k);++S){
            memcpy(pa,pre,sizeof pa);
            bool can=1;
            for(i=0;i<k;++i)if((S>>i)&1){if(find(pa,i+1)==find(pa,k+1))can=0;else merge(pa,i+1,k+1);}
            if(!can)continue;
             
            for(i=1;i<=k+1;++i)pa[i]=find(pa,i);
             
            memset(nowseq,0,sizeof nowseq);
            int id=0;
            for(i=1;i<=k+1;++i){
                if(nowseq[i]==0){
                    ++id;
                    for(j=i;j<=k+1;++j)if(pa[j]==pa[i])nowseq[j]=id;
                }
            }
             
            bool ok=0;
            for(i=2;i<=k+1;++i)if(nowseq[1]==nowseq[i]){ok=1;break;}
             
            if(ok){
                memset(nowseq,0,sizeof nowseq);
                id=0;
                for(i=2;i<=k+1;++i){
                    if(nowseq[i]==0){
                        ++id;
                        for(j=i;j<=k+1;++j)if(pa[j]==pa[i])nowseq[j]=id;
                    }
                }
                 
                int mi=1,re=0;
                for(i=2;i<=k+1;++i)re+=mi*nowseq[i],mi*=10;
                 
                if(M[re]==0)puts("WA");
                inc(add.d[t][M[re]],1);
            }
        }
    }
     
    n-=k;
    for(;n;n>>=1,add*=add)if(n&1)ans*=add;
     
    printf("%d",ans.d[1][1]);
     
    return 0;
}

BZOJ2331:[SCOI2011]地板 插头dp

思路:

我们将插头分为两种,一种是还没有拐弯的,另外一种是已经拐弯的.

转移就自己YY一下就行了.

注意边界条件.

 

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
#define R 110
#define C 110
int r,c;
bool Read[R][C],map[R][C];
 
char s[C];
 
static const int mod=20110520;
 
inline void inc(int&x,int y){if((x+=y)>=mod)x-=mod;}
 
struct State{
    int d[11];
    State(){memset(d,0,sizeof d);}
 
    inline int hash(){
        int re=0,mi=1;
        for(int i=0;i<=c;++i)re+=mi*d[i],mi*=3;
        return re;
    }
    inline int get(int ins){return d[ins];}
    inline State change(int ins,int x){State re=*this;re.d[ins]=x;return re;}
    inline void flush(){
        for(int i=c;i>=1;--i)d[i]=d[i-1];d[0]=0;
    }
     
    inline void print(){for(int i=0;i<=c;++i)printf("%d",d[i]);}
};
 
struct HashSet{
    int ins[200010],w[200010],ind;State S[200010];
 
    inline void reset(){memset(ins,-1,sizeof ins),ind=0;}
 
    inline void Insert(State s,int _w){
        //s.print(),printf(" %d\n",_w);
        int hash=s.hash();
        if(ins[hash]==-1)S[ind]=s,w[ind]=_w,ins[hash]=ind++;else inc(w[ins[hash]],_w);
    }
}set[2];
 
int main(){
    scanf("%d%d",&r,&c);register int i,j,k;
    for(i=1;i<=r;++i){
        scanf("%s",s+1);
        for(j=1;j<=c;++j)Read[i][j]=s[j]!='*';
    }
    if(r<c){for(i=1;i<=r;++i)for(j=1;j<=c;++j)map[j][i]=Read[i][j];swap(r,c);}
    else for(i=1;i<=r;++i)for(j=1;j<=c;++j)map[i][j]=Read[i][j];
 
    int now=0,last=1;
 
    State begin;set[now].reset(),set[now].Insert(begin,1);
 
    State nows;int noww,d1,d2;
    for(i=1;i<=r;++i){
        for(j=1;j<=c;++j){
            swap(now,last),set[now].reset();
 
            for(k=0;k<set[last].ind;++k){
                nows=set[last].S[k],noww=set[last].w[k],d1=nows.get(j-1),d2=nows.get(j);
 
                if(!map[i][j]){
                    if(d1==0&&d2==0)set[now].Insert(nows,noww);
                }
                else{
                    if(d1==0&&d2==0){
                        if(i!=r&&j!=c)set[now].Insert(nows.change(j-1,2).change(j,2),noww);
                        if(i!=r)set[now].Insert(nows.change(j-1,1).change(j,0),noww);
                        if(j!=c)set[now].Insert(nows.change(j-1,0).change(j,1),noww);
                    }
                    if(d1==1&&d2==0){
                        if(j!=c)set[now].Insert(nows.change(j-1,0).change(j,1),noww);
                        if(i!=r)set[now].Insert(nows.change(j-1,2).change(j,0),noww);
                    }
                    if(d1==0&&d2==1){
                        if(i!=r)set[now].Insert(nows.change(j-1,1).change(j,0),noww);
                        if(j!=c)set[now].Insert(nows.change(j-1,0).change(j,2),noww);
                    }
                    if(d1==2&&d2==0){
                        if(j!=c)set[now].Insert(nows.change(j-1,0).change(j,2),noww);
                        set[now].Insert(nows.change(j-1,0).change(j,0),noww);
                    }
                    if(d1==0&&d2==2){
                        if(i!=r)set[now].Insert(nows.change(j-1,2).change(j,0),noww);
                        set[now].Insert(nows.change(j-1,0).change(j,0),noww);
                    }
                    if(d1==1&&d2==1)set[now].Insert(nows.change(j-1,0).change(j,0),noww);
                }
            }
        }
 
        swap(now,last),set[now].reset();
        for(k=0;k<set[last].ind;++k){
            nows=set[last].S[k],noww=set[last].w[k],nows.flush();
            set[now].Insert(nows,noww);
        }
 
    }
 
    int res=0;for(k=0;k<set[now].ind;++k)inc(res,set[now].w[k]);
 
    printf("%d",res);
 
    return 0;
}

BZOJ1187:[HNOI2007]神奇游乐园 插头dp

思路:

非常裸的插头dp,只不过这道题是只有一条回路,这个很容易实现,当遇到左右插头合并的时候直接更新答案,而并不是向下转移.

然后这个是要维护一个左右插头,我们用三进制来维护.

具体转移时有几个细节:

不能在边界上插插头!

更新答案时必须确认当前只有左右插头各一个!

我写了一个结构体来维护插头,还是蛮好写的.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;

int l;
struct Line{
	int d[7];
	Line(){memset(d,0,sizeof d);}
	inline int hash(){
		int re=0,mi=1;for(int i=0;i<l;++i,mi*=3)re+=d[i]*mi;return re;
	}
	inline void flush(){
		for(int i=l-1;i>=1;--i)d[i]=d[i-1];d[0]=0;
	}
	inline int getleftins(int x){
		int sav=0;
		for(int i=x;i>=0;--i){
			if(d[i]==2)++sav;else if(d[i]==1)--sav;
			if(sav==0)return i;
		}
	}
	inline int getrightins(int x){
		int sav=0;
		for(int i=x;i<l;++i){
			if(d[i]==1)++sav;else if(d[i]==2)--sav;
			if(sav==0)return i;
		}
	}
	Line change(int ins,int x){
		Line re=*this;re.d[ins]=x;return re;
	}
	inline int getlnum(){
		int re=0;for(int i=0;i<l;++i)re+=d[i]==1;return re;
	}
	inline int getrnum(){
		int re=0;for(int i=0;i<l;++i)re+=d[i]==2;return re;
	}
};
struct Hashset{
	int ins[2187],w[2187],ind;
	Line s[2187];
	inline void reset(){ind=0,memset(ins,-1,sizeof ins);}
	inline void insert(Line l,int _w){
		int hash=l.hash();
		if(ins[hash]==-1)ins[hash]=ind,w[ind]=_w,s[ind++]=l;
		else w[ins[hash]]=max(w[ins[hash]],_w);
	}
}S[2];

#define N 110
#define M 10
int w[N][M];

int main(){
	int n,m;scanf("%d%d",&n,&m);register int i,j,k;l=m+1;
	for(i=1;i<=n;++i)for(j=1;j<=m;++j)scanf("%d",&w[i][j]);
	
	int now=0,last=1;
	Line begin;S[now].reset(),S[now].insert(begin,0);
	
	int res=-1<<30;
	Line nowst;int nowf,d1,d2,ins1,ins2;
	for(i=1;i<=n;++i){
		for(j=1;j<=m;++j){
			swap(now,last),S[now].reset();
			for(k=0;k<S[last].ind;++k){
				nowst=S[last].s[k];nowf=S[last].w[k];
				d1=nowst.d[j-1],d2=nowst.d[j];
				if(d1==0&&d2==0){
					S[now].insert(nowst,nowf);
					if(i!=n&&j!=m)S[now].insert(nowst.change(j-1,1).change(j,2),nowf+w[i][j]);
				}
				else if(d1==0||d2==0){
					if(i!=n)S[now].insert(nowst.change(j-1,d1+d2).change(j,0),nowf+w[i][j]);
					if(j!=m)S[now].insert(nowst.change(j-1,0).change(j,d1+d2),nowf+w[i][j]);
				}
				else if(d1==1&&d2==1){
					ins1=nowst.getrightins(j),ins2=nowst.getrightins(j-1);
					S[now].insert(nowst.change(j-1,0).change(j,0).change(ins1,1).change(ins2,2),nowf+w[i][j]);
				}
				else if(d1==1&&d2==2){
					if(nowst.getlnum()==1&&nowst.getrnum()==1)res=max(res,nowf+w[i][j]);
				}
				else if(d1==2&&d2==1){
					S[now].insert(nowst.change(j-1,0).change(j,0),nowf+w[i][j]);
				}
				else if(d1==2&&d2==2){
					ins1=nowst.getleftins(j),ins2=nowst.getleftins(j-1);
					S[now].insert(nowst.change(j-1,0).change(j,0).change(ins1,1).change(ins2,2),nowf+w[i][j]);
				}
			}
		}
		swap(now,last),S[now].reset();
		for(k=0;k<S[last].ind;++k){
			nowst=S[last].s[k],nowf=S[last].w[k];
			nowst.flush();
			S[now].insert(nowst,nowf);
		}
	}
	
	printf("%d",res);
	
	return 0;
}