Codeforces 1213G Path Queries

cf题面

中文题面

给一棵无根树,每条边有边权。然后q个询问,每次询问给个w,求树上有多少对点之间的路径上的最大值小于等于w。

解题思路

离线。先把所有边按照边长升序排序,再把所有询问按照w升序排序。

之后从小到大处理每个询问。对于一个询问,首先由于询问已经排好序了,所以前一个答案是之前加的边对于答案的贡献,我们就先把上一个询问的答案直接复制过来,之后把小于等于这个询问的w的所有边加入到树上,然后并查集更新答案:每加一条边,对答案产生的贡献是“这条边两端的连通块”大小之积。

之后恢复顺序,输出,没了。

虚拟赛过程中看见这题的时候,想不到用并查集,而是想着深搜(类似这个),对于一条边,讨论它下方的子树和上方树的其他部分的情况,但上方没想出来怎么处理,因为可能上方存在权值更大的边,不能一整个乘下去……然后想到点分治树分治啥的,全是xjb想……去看了这题的标签,dsu(并查集)、分治、排序。开始不知道啥是dsu,去百度找到了个dsu on tree,点进去发现时启发式合并,和这个没啥关系……

源代码

#include<cstdio>
#include<algorithm>

const int MAXN=2e5+5;
int n,m;

struct Que{
    int id,w;
    long long ans;
}q[MAXN];
bool cmp1(Que & a,Que & b){return a.w<b.w;}
bool cmp2(Que & a,Que & b){return a.id<b.id;}
struct Edge{
    int u,v,w;
    bool operator < (const Edge & x)const{
        return w<x.w;
    }
}e[MAXN];

int fa[MAXN],sz[MAXN];
int find(int x)
{
    return fa[x]=fa[x]==x?x:find(fa[x]);
}
void uni(int x,int y)
{
    x=find(x);
    y=find(y);
    fa[x]=y;
    sz[x]+=sz[y];
    sz[y]=sz[x];
}

int main()
{
//  freopen("test.in","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    }
    for(int i=0;i<m;i++)
    {
        scanf("%d",&q[i].w);
        q[i].id=i;
        q[i].ans=0;
    }
    std::sort(e+1,e+n);
    std::sort(q,q+m,cmp1);
    for(int i=1;i<=n;i++) fa[i]=i,sz[i]=1;
    for(int i=0,pos=1;i<m;i++)
    {
        q[i].ans=q[i-1].ans;
        while(pos<n&&e[pos].w<=q[i].w)
        {
            int u=e[pos].u,v=e[pos].v;
            q[i].ans+=1LL*sz[find(u)]*sz[find(v)];
            uni(u,v);
            pos++;
        }
    }
    std::sort(q,q+m,cmp2);
    for(int i=0;i<m;i++) printf("%lld ",q[i].ans);
    return 0;
}

原文地址:https://www.cnblogs.com/wawcac-blog/p/11466151.html

时间: 2024-09-30 13:23:55

Codeforces 1213G Path Queries的相关文章

codeforces 245H H. Queries for Number of Palindromes(区间dp)

题目链接: codeforces 245H 题目大意: 给出一个字符串,询问任意区间内的回文子串的个数. 题目分析: 定义isPar[i][j]表示区间字符串[i,j]是否是回文,可以通过isPar[i+1][j-1]递推得到. 定义dp[i][j]表示及区间[i,j]内的回文子串的个数,转移方程如下: dp[i][j]=dp[i+1][j]+dp[i][j?1]?dp[i+1][j?1]+isPar[i][j] 用到了一点容斥的思想. AC代码: #include <iostream> #i

CodeForces 932B Recursive Queries

Description Let us define two functions f and g on positive integer numbers. You need to process Q queries. In each query, you will be given three integers l, r and k. You need to print the number of integers xbetween l and r inclusive, such that g(x

Codeforces 817F - MEX Queries

817F - MEX Queries 思路: 离线+离散化+线段树 代码: #include<bits/stdc++.h> using namespace std; #define LL long long #define pb push_back #define ls rt<<1,l,m #define rs rt<<1|1,m+1,r const int N=1e5+5; int t[N],tree[N*16],lazy[N*16]; LL l[N],r[N]; v

「Codeforces」245H Queries for Number of Palindromes (区间dp)

题意:原题在这 You've got a string s = s1s2... s|s| of length |s|, consisting of lowercase English letters. There also are q queries, each query is described by two integers li, ri (1 ≤ li ≤ ri ≤ |s|). The answer to the query is the number of substrings of

CodeForces 825G&quot;Tree Queries&quot;(建树选根)

传送门 •参考资料 [1]:CodeForces 825G Educational Round #25 G :建树选根大法+O1大法+iostream解绑了还是慢 •题意 给定一颗包含 n 个节点的树,开始树的所有节点都是白色的: 给出 q 次询问,询问分为1.2两种: 将节点 x 涂成黑色. 询问节点 x 到所有的黑点节点的简单路径中的标号最小的那个点(包括起点和黑点) 题目保证第一次询问是 1 类型的. •题解 如果我们随便选取某节点作为根节点,那么询问的时候,我们要找到节点 x 到所有黑色

前缀和的应用 CodeForces - 932B Recursive Queries

题目链接: https://vjudge.net/problem/1377985/origin 题目大意就是要你把一个数字拆开,然后相乘. 要求得数要小于9,否则递归下去. 这里用到一个递归函数: int f(int x) { if(x < 10) return x; int ans = 1; while(x) { if(x%10 != 0) ans *= x%10; else ans *= 1; x /= 10; } return f(ans); } 这个函数用来求得一个数字最终得到的那个k值

CF 938G Shortest Path Queries

又到了喜闻乐见的写博客清醒时间了233,今天做的依然是线段树分治 这题算是经典应用了吧,假的动态图(可离线)问题 首先不难想到对于询问的时间进行线段树分治,这样就可以把每一条边出现的时间区间扔进线段树里,考虑如何维护答案 初步的想,图上两点间异或最小值,和最大值类似.先求出一棵生成树,然后把环扔进线性基里,每次查询两点间异或值之后再扔进线性基里求最小值即可 正确性的话,因为这样环一定是有树边+非树边构成的,我们可以在任意一个点走到一个环再绕回来,中间重复走的树边因为走了两次相当于没有影响 然后我

codeforces1213G Path Queries 并查集

题意 给定n个结点的树,每条边有边权,有m个询问,每个询问给一个\(q_i\)输出树上有多少点对的简单路径上最大的边权不超过\(q_i\). 分析 用并查集维护点集,同时维护大小. 将所有边按边权排序,考虑每次从小到大加边,图中经过当前边的所有路径一定是以当前边的边权为最大值的,用并查集维护下图中每个联通块的大小,经过当前边的路径数即为\(sz[find(u)]*sz[find(v)]\).然后前缀和一下就可以\(O(1)\)询问了. Code #include<bits/stdc++.h>

题解 CF938G 【Shortest Path Queries】

题目让我们维护一个连通无向图,边有边权,支持加边删边和询问从\(x\)到\(y\)的异或最短路. 考虑到有删边这样的撤销操作,那么用线段树分治来实现,用线段树来维护询问的时间轴. 将每一条边的出现时间段标记到线段树上,表示在这一段询问中这条边存在. 异或最短路的处理方法与最大XOR和路径类似,给线段树上每个节点开一个线性基,找出当前所有的环,插入该节点的线性基,查询时任意找出一条从\(x\)到\(y\)的路径,到线性基中查询,即为答案. 具体实现时用可撤销并查集,采用按秩合并来优化,因为路径压缩