Codeforces 1156D 0-1-Tree

传送:http://codeforces.com/contest/1156/problem/D

题意:有一棵$n$($n\leq200000$)个结点的树,$n-1$条边,每条边有一个值$(0,1)$,对于从$x$到$y$的唯一路径不能从0边到1边,问有多少点对符合要求。

分析:

  考虑这样一个dp方程,$dp[i][0/1]$,$dp[i][0]$代表从结点$i$出发的权值为0的边,可以到达点的个数,同理:$dp[i][1]$代表从结点$i$出发的权值为1的边,可以到达点的个数。

  那么就是说,我做两边树的遍历:

第一遍可以先处理出儿子继承父亲的答案;

第二遍处理出父亲“继承”儿子的答案(同时需要去除掉本身儿子继承父亲的答案)。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int maxn=2e5+10;
 5 struct node{
 6     int to,w,nxt;
 7 }e[maxn*2];
 8 int head[maxn],tot;
 9 ll ans;
10 ll dp[maxn][2];
11 void add(int x,int y,int w){
12     e[tot]={y,w,head[x]};
13     head[x]=tot++;
14 }
15 void dfs(int x,int fa){
16     for (int i=head[x];i!=-1;i=e[i].nxt){
17         if (e[i].to==fa) continue;
18         dfs(e[i].to,x);
19         if (e[i].w==0) dp[x][0]+=dp[e[i].to][0];
20         else dp[x][1]+=dp[e[i].to][0]+dp[e[i].to][1];
21     }
22 }
23 void dfs2(int x,int fa){
24     for (int i=head[x];i!=-1;i=e[i].nxt){
25         if (e[i].to==fa) continue;
26         if (e[i].w==0) dp[e[i].to][0]+=(dp[x][0]-dp[e[i].to][0]);
27         else dp[e[i].to][1]+=(dp[x][0]-dp[e[i].to][0])+(dp[x][1]-dp[e[i].to][1]);
28         dfs2(e[i].to,x);
29     }
30 } 
31 int main(){
32     int n,x,y,z; scanf("%d",&n);
33     tot=0;
34     for (int i=1;i<=n;i++) head[i]=-1;
35     for (int i=0;i<n-1;i++){
36         scanf("%d%d%d",&x,&y,&z);
37         add(x,y,z);
38         add(y,x,z);
39         dp[x][z]++;
40     }
41     ans=0;
42     dfs(1,0);
43     dfs2(1,0);
44     for (int i=1;i<=n;i++){
45         cout << dp[i][0] << "  " << dp[i][1] << endl;
46         ans+=(dp[i][0]+dp[i][1]);
47     }
48     printf("%lld\n",ans);
49     return 0; 
50 }

 来自学妹的并查集做法:

维护两个块,一个全为1,一个全为0。全为1或者全为0的块内答案为num*(num-1);

同时如果一个点可以连接起全0块或全1块,那么答案为(num1-1)*(num2-1)。

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 const int maxn=2e5+10;
 5 int pre[2][maxn],num[2][maxn];
 6 int n,a,b,c;
 7 int find(int mark,int x)
 8 {
 9     return x==pre[mark][x]?x:pre[mark][x]=find(mark,pre[mark][x]); 
10 }
11 void merge(int mark,int x,int y)
12 {
13     int fx=find(mark,x),fy=find(mark,y);
14     if (fx!=fy)
15     {
16         pre[mark][fx]=fy;
17         num[mark][fy]+=num[mark][fx];
18     }
19 }
20 int main()
21 {
22     scanf("%d",&n);
23     for (int i=0;i<=n;i++) pre[0][i]=pre[1][i]=i,num[0][i]=num[1][i]=1;
24     for (int i=1;i<n;i++)
25     {
26         scanf("%d%d%d",&a,&b,&c);
27         merge(c,a,b);
28     }
29     ll ans=0;
30     for (int i=1;i<=n;i++)
31     {
32         if (pre[0][i]==i) ans+=1ll*num[0][i]*(num[0][i]-1);
33         if (pre[1][i]==i) ans+=1ll*num[1][i]*(num[1][i]-1);
34         int xx=find(0,i),yy=find(1,i);
35         ans+=1ll*(num[0][xx]-1)*(num[1][yy]-1);
36     }
37     printf("%lld\n",ans);
38     return 0;
39 } 

 

转载于:https://www.cnblogs.com/changer-qyz/p/10827258.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值