AT4352 [ARC101C] Ribbons on Tree

解析

其实想到了断边按连通块容斥的做法。
但不知道为啥觉得没前途弃了…
悲。

考虑容斥,设 f[x][i] 表示 x 子树内与 x 所连的联通块有 i 个节点的方案数,第三维朴素是记录连通块个数,但其实只需要记一个 0/1 表示奇偶性即可。
然后题解奥妙重重的把这一步都取了:断奇数次贡献负,断偶数次贡献正,那直接每次断边的时候把 dp 值取反即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
using namespace std;

const int N=5050;
const int mod=1e9+7;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}


inline ll ksm(ll x,ll k,int mod){
  ll res(1);
  while(k){
    if(k&1) res=res*x%mod;
    x=x*x%mod;
    k>>=1;
  }
  return res;
}

int n;

ll f[N][N],tmp[2][N],top[N];
vector<int>e[N];
ll w[N];
void dfs(int x,int fa){
  for(int to:e[x]){
    if(to==fa) continue;
    dfs(to,x);    
  }
  memset(tmp,0,sizeof(tmp));
  int now=1,pre=0,mx=1;
  tmp[now][1]=1;
  for(int to:e[x]){
    if(to==fa) continue;
    swap(now,pre);
    memset(tmp[now],0,sizeof(tmp[now]));
    for(int i=0;i<=top[to];i++){
      for(int j=1;j<=mx;j++){
	(tmp[now][i+j]+=f[to][i]*tmp[pre][j])%=mod;
      }
    }
    mx+=top[to];
  }
  top[x]=mx;
  for(int i=1;i<=mx;i++){
    f[x][i]=tmp[now][i];
    (f[x][0]+=mod-tmp[now][i]*w[i]%mod)%=mod;
  }
  return;
}

signed main(){
  #ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  #endif
  n=read();
  w[0]=1;
  for(int i=1;i<=n;i++) w[i]=(i&1)?0:w[i-2]*(i-1)%mod;
  for(int i=1;i<n;i++){
    int x=read(),y=read();
    e[x].push_back(y);
    e[y].push_back(x);
  }
  dfs(1,0);
  printf("%lld\n",mod-f[1][0]);
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值