把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P3349 [ZJOI2016]小星星

题面传送门
似乎是很平凡的idea了啊,而且这道题也不卡常啊。
容易想到一个暴力dp,就是设\(dp_{i,j,k}\)表示\(i\)点,有了\(j\)的状态,当前点为\(k\)的方案数,这样子是枚举子集要\(O(n^33^n)\)的过不掉。
然后接下来这个idea大概出自[SHOI2016]黑暗前的幻想乡(也不能说出自因为是同一年考的),就是枚举一个集合,这个集合内的数随便取,最后答案是\(\sum\limits_{i=1}^{2^n}{f_{i}[|i|=n]}-\sum\limits_{i=1}^{2^n}{f_{i}[|i|=n-1]}+\sum\limits_{i=1}^{2^n}{f_{i}[|i|=n-2]}……\)的样子。这样就可以\(O(n^32^n)\)转移了。
然后这个看上去很不可过所以考虑有什么办法快一点,考虑每次将枚举好的子集拿出来而不到dp的时候再判断这样可以将\(2^n\)变成\(2^{n-2}\)然后就可以过了。
code:

#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 17
#define mod 998244353
#define eps (1e-5)
#define U unsigned int
#define it iterator
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
using namespace std;
int n,m,k,x,y,A[N+5][N+5],B[N+5],Bh,Pus;ll Ans[N+5],dp[N+5][N+5],ToT;
struct yyy{int to,z;};
struct ljb{int head,h[N+5];yyy f[N+5<<1];I void add(int x,int y){f[++head]=(yyy){y,h[x]};h[x]=head;}}s;
I void dfs(int x,int last){
	yyy tmp;re int i,j,h;for(i=1;i<=Bh;i++)dp[x][i]=1;for(i=s.h[x];i;i=tmp.z){
		tmp=s.f[i];if(tmp.to==last) continue;dfs(tmp.to,x);for(j=1;j<=Bh;j++){
			for(ToT=0,h=1;h<=Bh;h++) A[B[j]][B[h]]&&(ToT+=dp[tmp.to][h]);dp[x][j]*=ToT;
		}
	}
}
int main(){
	freopen("1.in","r",stdin);
	re int i,j;scanf("%d%d",&n,&m);for(i=1;i<=m;i++) scanf("%d%d",&x,&y),A[x][y]=A[y][x]=1;for(i=1;i<n;i++) scanf("%d%d",&x,&y),s.add(x,y),s.add(y,x);for(i=0;i<(1<<n);i++){
		for(Bh=0,j=1;j<=n;j++)(i>>j-1)&1&&(B[++Bh]=j);Me(dp,0);dfs(1,0);for(j=1;j<=Bh;j++) Ans[Bh]+=dp[1][j];//printf("%d %lld %d\n",Bh,Ans[Bh],i);
	}for(Pus=1,ToT=0,i=n;i;i--,Pus*=-1)ToT+=Pus*Ans[i];printf("%lld\n",ToT);
}
posted @ 2021-08-05 22:34  275307894a  阅读(32)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end