首页 > 其他分享 >CodeForces - 1156D 0-1-Tree

CodeForces - 1156D 0-1-Tree

时间:2022-11-11 16:55:11浏览次数:59  
标签:cnt 子树 int Tree 1156D CodeForces ans now dp

题意:给出一棵树,树的边权只有0和1。求有多少有序点对,其最短路径上每条权值为0的边不紧跟在权值为1的边后面。

解:合法路径如下所示:

000000 
111111 
000111 

随便找个结点为根节点,形成一棵树。结点ai为其中一棵子树Ti的根。考虑统计以结点ai为终点,最后一条边为0/1的路径数量。Ti的每棵子树内答案可以直接得到,子树间就是把两条路径拼起来。因此再添加一个不合法状态111000用来转移。给几个状态编号(从左往右为树中从下到上):

考虑要不要换根的问题。如果将ai作为整棵树的根,那它的原来的父节点就会成为它的一棵子树的根。这颗子树也应该和原来的子树拼一拼。但这种情况会在处理祖先节点的时候覆盖掉,所以dfs一遍,考虑目前它几棵子树的关系就可以了。

接下来拼几棵子树的路径。假设一棵子树和ai之间边wi的边权为1,那么这颗子树可以是状态0、1和2;状态0加上wi变成状态2,能和状态1拼;状态1能和状态1、状态0、状态2拼,状态2能和状态1拼。边权为1同理。

代码:

#include <bits/stdc++.h>
using namespace std;
#define maxx 200005
#define maxn 25
#define maxm 205
#define ll long long
#define inf 1000000009
#define mod 2520
struct edge{
    int u,v,w,next;
}a[maxx*2];
int head[maxx]={0},cnt=0;
void add(int u,int v,int w){
    a[++cnt].u=u;a[cnt].v=v;a[cnt].w=w;a[cnt].next=head[u];head[u]=cnt;
}

ll ans=0;
int n;
ll dp[maxx][4]={0};
//000000  0
//111111  1
//000111  2
//111000  3
void dfs1(int now,int fa){
    for(int i=head[now];i;i=a[i].next){
        int to=a[i].v;
        if(to==fa) continue;
        dfs1(to,now);
        if(a[i].w==1){
            ans+=dp[now][1]*(dp[to][1]+1)*2;
            ans+=dp[now][1]*dp[to][2];
            ans+=dp[now][1]*dp[to][0];
            ans+=dp[now][0]*(dp[to][1]+1);
            ans+=dp[now][2]*(dp[to][1]+1);

            dp[now][1]+=dp[to][1]+1;
            dp[now][2]+=dp[to][2];
            dp[now][2]+=dp[to][0];
        }
        else if(a[i].w==0){
            ans+=dp[now][0]*(dp[to][0]+1)*2;
            ans+=dp[now][0]*dp[to][3];
            ans+=dp[now][0]*dp[to][1];
            ans+=dp[now][1]*(dp[to][0]+1);
            ans+=dp[now][3]*(dp[to][0]+1);

            dp[now][0]+=dp[to][0]+1;
            dp[now][3]+=dp[to][1];
            dp[now][3]+=dp[to][3];
        }
    }
    ans+=dp[now][0]*2;
    ans+=dp[now][1]*2;
    ans+=dp[now][2];
    ans+=dp[now][3];
}
signed main() {
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    dfs1(1,0);
    printf("%lld\n",ans);
    return 0;
}
View Code

还有一种做法是统计一个结点ai能从边权为0/1的边出发,合法地到达多少个点。先从下到上dfs一遍,然后从上到下统计答案。

反正算起来都很绕啦

 

标签:cnt,子树,int,Tree,1156D,CodeForces,ans,now,dp
From: https://www.cnblogs.com/capterlliar/p/16881034.html

相关文章

  • Codeforces Round #697 (Div. 3) F
    F.UnusualMatrix这种题两种操作就相当于那种差分后再总体减的那种我们考虑先只进行一种操作比如说是行我们对于每一行应该只有可能经过0/1次变换都变成一摸一样的......
  • 动态构建TreeView(中国省市区)
    .aspx代码如下:<%@PageLanguage="C#"AutoEventWireup="true"CodeFile="中国市区县.aspx.cs"Inherits="中国市区县"%><!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0......
  • Codeforces Round #642 (Div. 3) E
    E.K-periodicGarland对于一个序列显然我们只有%m相同的位置上才能放置1不然肯定不合法所以我们把他分成m个部分记录一下总和然后转化一下题意发现他就是一个然......
  • Little Girl and Maximum Sum CodeForces - 276C - 差分
    给定一个数列\(a={a_1,a_2,...,a_n}\)以及\(q\)次查询。其中第\(i\)次查询如同:\(l_i,r_i\),意指求\(\sum_{j=l_i}^{r_i}{a_j}\)。但是查询前可以对数列任意排......
  • 「题解」Codeforces 1098D Eels
    暴力是,每次挑出最小的两个合并。需要观察到没有产生贡献的次数很小。考虑最小的那个数的大小,如果一次合并没有产生贡献,那么最小的数至少\(\times2\).所以最多会有\(\mat......
  • E. Paimon Segment Tree
    传送门题意:首先给出n,m,q,一个长度为n的数组,m次修改操作,每次修改操作,l,r,x,对l,r区间里面的数加上k,q次询问操作,每次询问操作,问\(\sum\limits_{i=lk}^{rk}\s......
  • Codeforces Round #697 (Div. 3) G
    G.StrangeBeauty观察性质我们发现他就是一个单向的关系要是我们3能被9整除那我们来一个能整除9的那么一定能整除3就是这个性质我们考虑dpdp[i]表示我们以a[i]结......
  • 涨姿势 之 Sourcetree 显示头像
    LZ-Says:莫名情愫,嗷呜前言生命不止,折腾不止,哇咔咔。目前Git较为好用的莫过于Sourcetree,简单方便快速,666,而今天在看Git提交记录时,突然感觉头像丑的一批,一起瞅瞅:左上......
  • 软件篇 之 Mac Sourcetree 安装使用
    LZ-Says:今天安装网,怎一个曲折。不过从而也明白了信任,感谢。前言新系统,安装、配置好多东西,有点乱。这里简单记录下过程,避免后续脑子2333白白浪费时间。点滴积累,加油开撸......
  • CodeForces - 708C Centroids
    题意:给出一棵有n个结点的树,对于每一个结点,如果任意删除一条边后再任意添加一条边能使这个结点成为这棵树的重心,则输出1;反之输出0。解:重心的特点:以重心为根节点时,其最大子......