zoukankan      html  css  js  c++  java
  • [loj#2091] [ZJOI2016] 小星星

    题意简述

    给定一个 (n) 个点 (m) 条边的无向图,再给定一棵 (n) 个点的树。
    要把树中的点与无向图中的点一一对应,要求树上有连边的点所对应的点在图上也有连边。
    问有多少种对应方式。

    (nleq 17)


    想法

    首先,我觉得这题应有 (bgm)

    你 就是我的小星星
    挂 在那天上放光明
    我已经决定要爱你
    就不会轻易放弃

    哒哒哒~

    言归正传!
    先考虑树形 (dp)(f[i][j][s]) 表示 (i) 与原图中 (j) 对应时,以 (i) 为根的子树与 (s) 中的点对应的方案数((s) 为状压)
    但这样过不去。

    发现这里最麻烦的就是 (s) ,于是把它容斥掉。
    (g[s]) 表示树上所有点只与 (s) 中的点对应的方案数。
    在此(s) 下,(f[i][j]) 表示 (i) 与原图中 (j) 对应时,以 (i) 为根的子树与 (s) 中的点对应的方案数
    有转移方程 (f[i][j]=prodlimits_{fa[v]=i} (sumlimits_{j与k在原图中有边} f[v][k]))
    显然 (g[s]=sumlimits_{iin s}f[root][i])

    容斥很经典,(ans=sum(-1)^{n-|s|} imes g[s])


    总结

    树上问题,先考虑树形 (dp) !别先想那些奇奇怪怪的做法!
    神仙容斥……


    代码

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    int read(){
    	int x=0;
    	char ch=getchar();
    	while(ch<'0' || ch>'9') ch=getchar();
    	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    	return x;
    }
    
    const int N = 18;
    typedef long long ll;
    
    int mp[N][N],con[N][N],n,m;
    struct node{
    	int v;
    	node *nxt;
    }pool[N*2],*h[N];
    int cnt;
    void addedge(int u,int v){
    	node *p=&pool[++cnt];
    	p->v=v;p->nxt=h[u];h[u]=p;
    }
    
    ll f[N][N];
    int fa[N];
    void dfs(int u){
    	for(int v=1;v<=n;v++)
    		if(con[u][v] && !fa[v]) {
    			fa[v]=u; addedge(u,v);
    			dfs(v);
    		}
    }
    
    int ok[N];
    void cal(int u){
    	int v;
    	ll t;
    	for(node *p=h[u];p;p=p->nxt) cal(p->v);
    	for(int i=1;i<=n;i++){
    		if(ok[i]^1) { f[u][i]=0; continue; }
    		f[u][i]=1;
    		for(node *p=h[u];p;p=p->nxt){
    			v=p->v; t=0;
    			for(int j=1;j<=n;j++) if(mp[i][j]) t+=f[v][j];
    			f[u][i]*=t; 
    		}
    	}
    }
    
    int main()
    {
    	int u,v;
    	n=read(); m=read();
    	for(int i=0;i<m;i++){
    		u=read(); v=read();
    		mp[u][v]=mp[v][u]=1;
    	}
    	for(int i=1;i<n;i++) {
    		u=read(); v=read();
    		con[u][v]=con[v][u]=1;
    	}
    	
    	fa[1]=-1; dfs(1);
    	int w=(1<<n),t;
    	ll ans=0,cur;
    	for(int s=1;s<w;s++){
    		t=0; cur=0;
    		for(int i=0;i<n;i++) 
    			ok[i+1]=(1&(s>>i)),t+=ok[i+1];
    		cal(1);
    		for(int i=1;i<=n;i++) cur+=f[1][i];
    		ans+=cur*(((n-t)&1) ? (-1) : 1);
    	}
    	printf("%lld
    ",ans);
    	
    	return 0;
    }
    
  • 相关阅读:
    Python札记 装饰器
    Python与SQLite日期时间函数的使用
    Python札记 文件校验
    Python札记 参数魔法
    JAVA操作图片裁切与缩放的一个工具类
    java对象的序列化和反序列化
    Apache MINA 初识
    在Java 7里如何对文件进行操作
    Calendar时间获取明细
    使用ViewPager实现欢迎页面左右拖动效果
  • 原文地址:https://www.cnblogs.com/lindalee/p/12404753.html
Copyright © 2011-2022 走看看