[ZJOI2016]小星星&[SHOI2016]黑暗前的幻想乡(容斥)

这两道题思路比较像,所以把他们放到一块。

[ZJOI2016]小星星

题目描述

小Y是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有n颗小星星,用m条彩色的细线串了起来,每条细线连着两颗小星星。

有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了n-1条细线,但通过这些细线,这颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。小Y找到了这个饰品的设计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,那么要求对应的小星星原来的图纸上也有细线相连。小Y想知道有多少种可能的对应方式。

只有你告诉了她正确的答案,她才会把小饰品做为礼物送给你呢。

题解

做容斥题有一个基本模型,就是有一个限制,我们直接在转移或者统计复杂度过高,但如果把它放宽一点的话复杂度会降低许多。

然后总的条件数也支持2^n枚举,就可以去考虑容斥。

这个题是说,有一个n个点的无向图和n个点的一棵树,问有多少种一一对应的映射使得在树中有的边,图中也有。

看到树可以联想树形dp,因为我们要求一一对应,所以我们可以考虑设dp[i][j][s]表示以i为根的子树,i对应了图中的j,i子树对应了图中的集合s的方案数。

转移还是比较简单的。

我们观察到这个算法复杂度瓶颈在于枚举s,所以我们考虑能不能去掉。

去掉之后会出现树中的多个点对应了图中的一个点,方案数会算多。

怎么办?这个形式其实已经很明显了,直接套用容斥公式算就好了。

2^n枚举图中选那些点,然后做二维的树形dp就好了。

代码

#include<iostream>
#include<cstdio>
#define N 18
#define R register
using namespace std;
typedef long long ll;
int n,m,tot,head[N],cou[1<<N];
ll dp[N][N],ans;
bool a[N][N],jin[N];
inline int rd(){
    int x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c==‘-‘)f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
struct edge{int n,to;}e[N*N*2];
inline void add(int u,int v){
    e[++tot].n=head[u];e[tot].to=v;head[u]=tot;
}
void dfs(int u,int fa){
    for(R int i=1;i<=n;++i)if(!jin[i])dp[u][i]=1;else dp[u][i]=0;
    for(R int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
        int v=e[i].to;dfs(v,u);
        for(R int j=1;j<=n;++j)if(!jin[j]){
          ll num=0;
          for(R int k=1;k<=n;++k)if(a[j][k]&&!jin[k])num+=dp[v][k];
          dp[u][j]*=num;
        }
    }
}
int main(){
   n=rd();m=rd();int x,y;
   for(R int i=1;i<=m;++i){
         x=rd();y=rd();
         a[x][y]=a[y][x]=1;
   }
   for(R int i=1;i<n;++i){x=rd();y=rd();add(x,y);add(y,x);}
   for(R int i=0;i<(1<<n);++i){
         cou[i]=cou[i>>1]+(i&1);
         for(R int j=1;j<=n;++j)jin[j]=(i&(1<<j-1))!=0;
         dfs(1,0);
         ll num=0;
         for(R int j=1;j<=n;++j)num+=dp[1][j];
         if(cou[i]&1)ans-=num;else ans+=num;
   }
   cout<<ans;
   return 0;
}

[SHOI2016]黑暗前的幻想乡

题目描述

四年一度的幻想乡大选开始了,最近幻想乡最大的问题是很多来历不明的妖怪涌入了幻想乡,扰乱了幻想乡昔日的秩序。但是幻想乡的建制派妖怪(人类)博丽灵梦和八云紫等人整日高谈所有妖怪平等,幻想乡多元化等等,对于幻想乡目前面临的种种大问题却给不出合理的解决方案。

风见幽香是幻想乡里少有的意识到了问题严重性的大妖怪。她这次勇敢地站了出来参加幻想乡大选,提出包括在幻想乡边境建墙(并让人类出钱),大力开展基础设施建设挽回失业率等一系列方案,成为了大选年出人意料的黑马并顺利地当上了幻想乡的大统领。

幽香上台以后,第一项措施就是要修建幻想乡的公路。幻想乡一共有 nn 个城市,之前原来没有任何路。幽香向选民承诺要减税,所以她打算只修 n-1n?1条公路将这些城市连接起来。但是幻想乡有正好 n-1n?1 个建筑公司,每个建筑公司都想在修路地过程中获得一些好处。虽然这些建筑公司在选举前没有给幽香钱,幽香还是打算和他们搞好关系,因为她还指望他们帮她建墙。所以她打算让每个建筑公司都负责一条路来修。

每个建筑公司都告诉了幽香自己有能力负责修建的路是哪些城市之间的。所以幽香打算 n - 1n?1条能够连接幻想乡所有城市的边,然后每条边都交给一个能够负责该边的建筑公司修建,并且每个建筑公司都恰好修建一条边。

幽香现在想要知道一共有多少种可能的方案呢?两个方案不同当且仅当它们要么修的边的集合不同,要么边的分配方式不同。

题解

这题和上一题相似,有两个限制。

上一题是要满足树中和图中都要有某条边。

这题是要满足我们的生成树中,既要有n个不同的点,还要满足每种颜色的边个出现一次。

如果我们去掉第二个限制,那就变成了生成树计数问题,套用矩阵树定理即可。

然后我们发现面前的这个问题还是可以套用容斥公式直接计算的,于是这道题被愉快的解决了。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 18
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll a[N][N];
int n,m,cou[1<<N];
bool tag[N];
inline int rd(){
    int x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c==‘-‘)f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
inline ll power(ll x,ll y){
    ll ans=1;
    while(y){if(y&1)ans=ans*x%mod;x=x*x%mod;y>>=1;}
    return ans;
}
struct node{int x,y;};
vector<node>vec[N];
inline ll ni(ll x){return power(x,mod-2);}
inline ll gauss(ll tot){
    ll ans=1;
    for(int i=1;i<=tot;++i)
     for(int j=i+1;j<=tot;++j){
         ll t=a[j][i]*ni(a[i][i])%mod;
         for(int k=i;k<=tot;++k)a[j][k]=((a[j][k]-t*a[i][k])%mod+mod)%mod;
     }
    for(int i=1;i<=tot;++i)ans=(ans*a[i][i]%mod+mod)%mod;
    return ans;
}
inline ll work(){
    memset(a,0,sizeof(a));
    for(int i=1;i<n;++i)if(tag[i]){
        for(int j=0;j<vec[i].size();++j){
            int x=vec[i][j].x,y=vec[i][j].y;
            a[x][x]++;a[y][y]++;a[x][y]--;a[y][x]--;
        }
    }
    return gauss(n-1);
}
int main(){
    n=rd();int x,y;
    for(int i=1;i<n;++i){
      m=rd();
      for(int j=1;j<=m;++j){
        x=rd();y=rd();vec[i].push_back(node{x,y});
      }
    }
    ll ans=0;
    for(int i=0;i<(1<<n-1);++i){
        cou[i]=cou[i>>1]+(i&1);
        for(int j=1;j<=n-1;++j)tag[j]=(i&(1<<j-1))==0;
        if(cou[i]&1)ans-=work();else ans+=work();
        ans=(ans%mod+mod)%mod;
    }
    cout<<ans;
    return 0;
}

原文地址:https://www.cnblogs.com/ZH-comld/p/10234938.html

时间: 2024-09-28 16:24:21

[ZJOI2016]小星星&[SHOI2016]黑暗前的幻想乡(容斥)的相关文章

BZOJ 4596: [Shoi2016]黑暗前的幻想乡

4596: [Shoi2016]黑暗前的幻想乡 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 408  Solved: 232[Submit][Status][Discuss] Description 四年一度的幻想乡大选开始了,最近幻想乡最大的问题是很多来历不明的妖 怪涌入了幻想乡,扰乱了幻想乡昔日的秩序.但是幻想乡的建制派妖怪(人类) 博丽灵梦和八云紫等人整日高谈所有妖怪平等,幻想乡多元化等等,对于幻想乡 目前面临的种种大问题却给不出合适的解

bzoj4596[Shoi2016]黑暗前的幻想乡 Matrix定理+容斥原理

4596: [Shoi2016]黑暗前的幻想乡 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 464  Solved: 264[Submit][Status][Discuss] Description 四年一度的幻想乡大选开始了,最近幻想乡最大的问题是很多来历不明的妖 怪涌入了幻想乡,扰乱了幻想乡昔日的秩序.但是幻想乡的建制派妖怪(人类) 博丽灵梦和八云紫等人整日高谈所有妖怪平等,幻想乡多元化等等,对于幻想乡 目前面临的种种大问题却给不出合适的解

P4336 [SHOI2016]黑暗前的幻想乡

题目 P4336 [SHOI2016]黑暗前的幻想乡 做法 每种颜色恰好一条边,有点难处理啊 根据套路,数据范围这么小,容斥一下所有的情况就可以了 对每种颜色进行状压,表这次只能选这些颜色,做\(n-1\)的时候会加上多余的东西,就减去\(n-2......\) 剩下的交给矩阵树,这题有模数,高斯消元的时候逆元 My complete code #include<cstring> #include<string> #include<iostream> #include&

bzoj4596/luoguP4336 [SHOI2016]黑暗前的幻想乡(矩阵树定理,容斥)

bzoj4596/luoguP4336 [SHOI2016]黑暗前的幻想乡(矩阵树定理,容斥) bzoj Luogu 题解时间 看一看数据范围,求生成树个数毫无疑问直接上矩阵树定理. 但是要求每条边都属于不同公司就很难直接实现. 按套路上容斥: 如果直接将几个公司的修路列表加进矩阵里的话,求出来的是"只使用"这些边的生成树个数. 很明显上容斥之后就会直接变成"只使用"且"每个都被使用"的个数. 正好符合题目要求的生成树的n-1条边分属于n-1个公

BZOJ4596: [Shoi2016]黑暗前的幻想乡

Description 四年一度的幻想乡大选开始了,最近幻想乡最大的问题是很多来历不明的妖 怪涌入了幻想乡,扰乱了幻想乡昔日的秩序.但是幻想乡的建制派妖怪(人类) 博丽灵梦和八云紫等人整日高谈所有妖怪平等,幻想乡多元化等等,对于幻想乡 目前面临的种种大问题却给不出合适的解决方案. 风间幽香是幻想乡里少有的意识到了问题的严重性的大妖怪.她这次勇敢的 站了出来参加幻想乡大选.提出包括在幻想乡边境建墙(并让人类出钱),大力 开展基础设施建设挽回失业率等一系列方案,成为了大选年出人意料的黑马并顺 利的当

[SHOI2016]黑暗前的幻想乡

Description 四年一度的幻想乡大选开始了,最近幻想乡最大的问题是很多来历不明的妖 怪涌入了幻想乡,扰乱了幻想乡昔日的秩序.但是幻想乡的建制派妖怪(人类) 博丽灵梦和八云紫等人整日高谈所有妖怪平等,幻想乡多元化等等,对于幻想乡 目前面临的种种大问题却给不出合适的解决方案. 风间幽香是幻想乡里少有的意识到了问题的严重性的大妖怪.她这次勇敢的 站了出来参加幻想乡大选.提出包括在幻想乡边境建墙(并让人类出钱),大力 开展基础设施建设挽回失业率等一系列方案,成为了大选年出人意料的黑马并顺 利的当

【bzoj4596】[Shoi2016]黑暗前的幻想乡

Description 四年一度的幻想乡大选开始了,最近幻想乡最大的问题是很多来历不明的妖 怪涌入了幻想乡,扰乱了幻想乡昔日的秩序.但是幻想乡的建制派妖怪(人类) 博丽灵梦和八云紫等人整日高谈所有妖怪平等,幻想乡多元化等等,对于幻想乡 目前面临的种种大问题却给不出合适的解决方案. 风间幽香是幻想乡里少有的意识到了问题的严重性的大妖怪.她这次勇敢的 站了出来参加幻想乡大选.提出包括在幻想乡边境建墙(并让人类出钱),大力 开展基础设施建设挽回失业率等一系列方案,成为了大选年出人意料的黑马并顺 利的当

●BZOJ 4596 [Shoi2016]黑暗前的幻想乡

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=4596 题解: 容斥,矩阵树定理,矩阵行列式 先说说容斥:(一共有 N-1个公司) 令 f[i] 表示选出 (N-1)-i 个公司来修路(即有i个公司一定不修),且不管每个公司只能修一条路这一限制的方案数.那么 答案 ANS=0个公司不修的方案数 - 1个公司不修的方案数 +2个公司不修的翻案数 ...即 ANS= f[0] - f[1] +f[2] - ... + (-1)i*f[i]f[

「SHOI2016」黑暗前的幻想乡

题目链接 戳我 \(Describe\) \(n?1\)个公司,每个公司能修一些边,求每条边都让不同的公司来修的生成树的方案数 \(Solution\) 这道题很明显容斥.答案就是:所有都选的生成树个数\(-\)一个没选的生成树个数\(+\)两个没选的生成树个数\(-...\) 至于生成树个数怎么算,用\(Matrix - Tree\)矩阵树定理做就好了 如果不会:传送门 \(Code\) #include<bits/stdc++.h> #define int long long #defin