码迷,mamicode.com
首页 > 其他好文 > 详细

CF891D Sloth

时间:2018-04-17 11:52:27      阅读:130      评论:0      收藏:0      [点我收藏+]

标签:push   algo   bsp   for   typedef   完美   div   就是   删掉   

题意:给你一棵树,你选择删掉一条边,再加上一条边(也要保证为树),问最后树上的节点能够两两完美匹配的加删边方案数?

n<=5e5.

 

标程:

 1 #include<cstdio>
 2 #include<vector>
 3 #include<algorithm>
 4 using namespace std;
 5 int read()
 6 {
 7    int x=0;char ch=getchar();
 8    while (ch<0||ch>9) ch=getchar();
 9    while (0<=ch&&ch<=9) x=(x<<1)+(x<<3)+ch-0,ch=getchar();
10    return x;
11 }
12 const int N=500005;
13 typedef long long ll;
14 int size[N],n,u,v;
15 ll ans;
16 vector<int> vec[N];
17 struct node{int a[2][2];}f[N],g[N];
18 vector<node> pre[N];
19 void init(node &x){x.a[0][0]=1;x.a[0][1]=x.a[1][0]=x.a[1][1]=0;}
20 node modi(node x,node y)
21 {
22     node c;
23     c.a[0][0]=x.a[0][0]*y.a[1][0];
24     c.a[1][0]=x.a[0][0]*y.a[0][0]+x.a[1][0]*y.a[1][0];
25     c.a[0][1]=x.a[0][1]*y.a[1][0]+x.a[0][0]*(y.a[1][1]+y.a[0][0]);
26     c.a[1][1]=x.a[1][1]*y.a[1][0]+x.a[1][0]*(y.a[1][1]+y.a[0][0])+x.a[0][0]*y.a[0][1]+x.a[0][1]*y.a[0][0];
27     return c;
28 }
29 node merge(node x,node y)
30 {
31    node c;
32    c.a[0][0]=x.a[0][0]*y.a[0][0];
33    c.a[1][0]=x.a[1][0]*y.a[0][0]+x.a[0][0]*y.a[1][0];
34    c.a[0][1]=x.a[0][1]*y.a[0][0]+x.a[0][0]*y.a[0][1];
35    c.a[1][1]=x.a[1][1]*y.a[0][0]+x.a[1][0]*y.a[0][1]+x.a[0][1]*y.a[1][0]+x.a[0][0]*y.a[1][1];
36    return c;
37 }
38 void dp1(int x,int fa)
39 {
40     size[x]=1;init(f[x]); 
41     if (fa!=-1) vec[x].erase(find(vec[x].begin(),vec[x].end(),fa));//将fa删去,方便前后缀的处理
42     for (int i=0;i<vec[x].size();i++)
43     {
44       int v=vec[x][i]; //注意内部定义
45       dp1(v,x);size[x]+=size[v];
46       f[x]=modi(f[x],f[v]);
47     }
48 }
49 void dp2(int x,int fa)
50 {
51     node cur;init(cur);
52     if (fa>0) pre[x].push_back(modi(cur,g[x]));else pre[x].push_back(cur);
53     for (int i=0;i<vec[x].size();i++)
54       pre[x].push_back(modi(pre[x].back(),f[vec[x][i]]));
55     for (int i=vec[x].size()-1;i>=0;i--)
56     {
57        int v=vec[x][i];
58        g[v]=merge(pre[x][i],cur);cur=modi(cur,f[v]);
59        dp2(v,x);
60     }
61 }
62 int main()
63 {
64     n=read();if (n&1) return puts("0"),0;
65     for (int i=1;i<n;i++) u=read(),v=read(),vec[u].push_back(v),vec[v].push_back(u);
66     dp1(1,-1);dp2(1,-1);
67     for (int i=2;i<=n;i++)
68       if (f[i].a[1][0]&&g[i].a[1][0]) ans+=(ll)size[i]*(n-size[i]);
69       else ans+=(ll)(f[i].a[0][0]+f[i].a[1][1])*(g[i].a[0][0]+g[i].a[1][1]);
70     printf("%lld\n",ans);
71     return 0;
72 }

 

易错点:1.转移式子要认真推。

2.注意函数内部定义变量。

3.将fa在vector中删去,方便前后缀寻址的对应。

 

题解:dp

把一条边删掉,树就分成了两个子树。要么各自匹配(这样怎么连都可以),要么连一条边后边的端点匹配,也就是两个子树在没有连边前各有一个点没有匹配。

f[i][0/1][0/1]表示以i为根的子树,根是否被匹配,子树中是否恰有一个点未被匹配的未匹配点的选法数。

g表示i为根的子树外的部分的答案(以fa[i]为根)。正反dp两遍后统计答案即可。

CF891D Sloth

标签:push   algo   bsp   for   typedef   完美   div   就是   删掉   

原文地址:https://www.cnblogs.com/Scx117/p/8861765.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!