P4796 题解
题目大意
本题题面 讲的比较清楚了,这里不细说。
思路
考虑状压 dp,
初始化:很显然
转移:先枚举状态,再枚举点进行转移,如果有两个点以上(即 S 中有 2 个 1 以上)就可以统计进答案。
易得转移方程:
代码
#include <bits/stdc++.h>//祝大家学习愉快!
#define int long long
using namespace std;
const int maxn=3e5+10;
struct edge{
int to,nxt;
}e[maxn<<1];
int head[maxn],n,m,k,s;
int dp[maxn][40],a[maxn];
int dt[maxn],cnt=0,ans=0;
void add(int u,int v){
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
int lowbit(int x){
return x&(-x);
}
int num1(int x){
int res=0;
while(x){
res++;
x-=lowbit(x);
}
return res;
}
bool cmp(int x,int y){
return num1(x)<num1(y);
}
inline void init(){
s=(1<<k)-1;
for(int i=1;i<=s;i++) dt[i]=i;
sort(dt+1,dt+s+1,cmp);
memset(dp,0,sizeof(dp));
memset(head,-1,sizeof(head));
}
void solve(){
for(int i=1;i<=n;i++) dp[i][1<<(a[i]-1)]=1;
for(int i=1;i<=s;i++){
int tmp=dt[i];
for(int j=1;j<=n;j++){
if(dp[j][tmp]){
if(num1(tmp)>=2) ans+=dp[j][tmp];
for(int k=head[j];k!=-1;k=e[k].nxt){
int v=e[k].to;
if(tmp&(1<<(a[v]-1))) continue;
dp[v][tmp|(1<<(a[v]-1))]+=dp[j][tmp];
}
}
}
}
}
signed main(){
scanf("%lld %lld %lld",&n,&m,&k);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
init();
for(int i=1;i<=m;i++){
int u,v;
scanf("%lld %lld",&u,&v);
add(u,v);
add(v,u);
}
solve();
printf("%lld\n",ans);
return 0;
}