• CF891D Sloth


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

    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
    ",ans);
    71     return 0;
    72 }

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

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

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

    题解:dp

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

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

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

  • 相关阅读:
    redis分布式锁原理
    设置linux源
    linux 设置固定ip 并且解决ping www.baidu.com不通问题
    java内存模型
    Messenger和MVVM中的View Services
    MVVMLight
    MVVMLight
    MvvmLight
    MVVM 事件转命令1
    Mvvm简介
  • 原文地址:https://www.cnblogs.com/Scx117/p/8861765.html
Copyright © 2020-2023  润新知