洛谷 U138348 间谍网
洛谷 U138348 间谍网
题目背景
SeawaySeawa**y所效命的AlphaAlpha国和邪恶的EulerEuler国间最终还是爆发了战争。战争爆发前,AlphaAlpha国就一直对EulerEuler国进行间谍渗透。现在战争爆发了,EulerEuler国大肆捕捉AlphaAlpha国的间谍们。为了在保护勇士们的安全的同时继续进行情报收集,SeawaySeawa**y被任命为AlphaAlpha国国家情报局局长,负责重构安插在EulerEuler国的间谍网......
题目描述
众所周知,每个间谍都有自己的上下线。间谍间的上下线关系构成了间谍网。显然,所有间谍都在同一个间谍网内。但是,这个网络非常冗杂。这种冗杂的上下线关系增加了间谍暴露的风险。现在,SeawaySeawa**y想知道,最少有多少对上下线关系能够保证间谍网的畅通。并且,在间谍网中,有一类间谍至关重要,他们被称作“末梢间谍”。他们只有一个上线,没有任何下线。SeawaySeawa**y非常关心这类间谍的数量。经过分析,SeawaySeawa**y认为,在构成整个间谍网的NN个间谍中,这样的间谍应该有恰好KK个。现在,你的任务是:在原始间谍网(NN个间谍,MM对上下线关系)中删除若干对上下线关系,留下最少的上下线关系保证间谍网畅通,并且满足有KK个末梢间谍。显然,这个任务有许多种解决方案。如果有一对上下线关系在第一种方案中被删除而在第二种方案中没被删除,那么我们认为这两个方案不同。你只需要给出全部的方案数。
输入格式
从文件warfare.inwarfare.i**n中读入数据。
第一行三个整数N,M,KN,M,K。接下来的MM行,每行两个整数u,vu,v,描述一对上下线关系。数据保证每对间谍之间不会有重复关系,原始间谍网必然畅通。
输出格式
输出到文件warfare.outwarfare.out中。
仅一行一个整数,表示满足条件的方案数。
题解:
看到这个数量级就是状压。但是这道题看似还跟状压搭不上什么边。所以就需要思维的一个转化。
一开始想状压肯定是想到压叶子节点的状态。但是什么叫叶子节点状态?就是当前节点是否为叶子节点。但是我们这么设状态没办法保证全树节点个数。所以还需要把整棵树的状态压进去,表示当前节点是否被选。所以状态就设置完成了。
\(dp[i][j]\)表示树的状态为\(i\),叶子节点状态为\(j\)时的方案数量。
初值就是只有一个节点的时候为1.
状态转移就是,对于一个新加入的节点,如果它插在了非叶子节点上,它将会成为新的叶子节点,此时总叶子数加一;如果它插在叶子节点上,原先的叶子节点会变成非叶子节点,新加入的节点会变成叶子节点。转移应该是更新状态后累加。
最后的难点是如何统计答案。
可以考虑枚举所有叶子节点状态,lowbit(j)==k的时候就累加答案。
应该是不用取模,答案并不会很大。
代码:
#include<cstdio>
#include<vector>
#define int long long
using namespace std;
const int maxs=(1<<11)+10;
vector<int> map[20];
int dp[maxs][maxs];
//dp[i][j]表示生成树状态为i,度数为1的点状态为j的方案数
int n,m,k,ans;
int lowbit(int x)
{
int tot=0;
while(x)
{
if(x&1)
tot++;
x>>=1;
}
return tot;
}
signed main()
{
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%lld%lld",&x,&y);
x--;y--;
map[x].push_back(y);
map[y].push_back(x);
}
for(int i=1;i<(1<<n);i<<=1)
dp[i][i]=1;
for(int i=1;i<(1<<n);i++)
for(int j=i;j;j--,j&=i)
if(dp[i][j])
for(int k=0;k<n;k++)
if(i&(1<<k))
for(int l=0;l<map[k].size();l++)
{
int to=map[k][l];
int now;
if(~i&(1<<to))
{
if(lowbit(i)==1)
now=i|(1<<to);
else
now=j&~(1<<k)|(1<<to);
if(!(now>>to+1))
dp[i|(1<<to)][now]+=dp[i][j];
}
}
int fi=(1<<n)-1;
for(int i=0;i<(1<<n);i++)
if(lowbit(i)==k)
ans+=dp[fi][i];
printf("%lld",ans);
return 0;
}