luogu P4178 Tree

传送门

板子ex

开始天真的以为把m组询问改成k组询问就行了

但是板子里面的两层循环求方案实在是接受不了

所以套一个树状数组统计答案就行

注意大于k的边权全都不要

Code:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #define ms(a,b) memset(a,b,sizeof a)
  5 #define rep(i,a,n) for(int i = a;i <= n;i++)
  6 #define per(i,n,a) for(int i = n;i >= a;i--)
  7 #define inf 2147483647
  8 using namespace std;
  9 typedef long long ll;
 10 #define B puts("GG")
 11 ll read() {
 12     ll as = 0,fu = 1;
 13     char c = getchar();
 14     while(c < ‘0‘ || c > ‘9‘) {
 15         if(c == ‘-‘) fu = -1;
 16         c = getchar();
 17     }
 18     while(c >= ‘0‘ && c <= ‘9‘) {
 19         as = as * 10 + c - ‘0‘;
 20         c = getchar();
 21     }
 22     return as * fu;
 23 }
 24 //head
 25 const int N = 100003;
 26 int n,m,q[N];
 27 int head[N],mo[N<<1],nxt[N<<1],cst[N<<1],cnt;
 28 void addd(int x,int y,int w) {
 29     nxt[++cnt] = head[x],head[x] = cnt;
 30     mo[cnt] = y,cst[cnt] = w;
 31 }
 32 void add(int x,int y) {
 33     int w = read();
 34     if(x^y) addd(x,y,w),addd(y,x,w);
 35 }
 36
 37 int sze[N];
 38 bool vis[N];
 39 int sum,rt,maxx[N];
 40 void getroot(int x,int f) {
 41     sze[x] = 1,maxx[x] = 0;
 42     for(int i = head[x];i;i = nxt[i]) {
 43         int sn = mo[i];
 44         if(vis[sn] || sn == f) continue;
 45         getroot(sn,x),sze[x] += sze[sn];
 46         maxx[x] = max(maxx[x],sze[sn]);
 47     }
 48     maxx[x] = max(maxx[x],sum - sze[x]);
 49     if(maxx[x] < maxx[rt]) rt = x;
 50 }
 51
 52 int dis[N],stk[N];
 53 void getdis(int x,int f) {
 54     // if(dis[x] > m) return;
 55     stk[++stk[0]] = dis[x];
 56     for(int i = head[x];i;i = nxt[i]) {
 57         int sn = mo[i];
 58         if(sn == f || vis[sn]) continue;
 59         dis[sn] = dis[x] + cst[i];
 60         getdis(sn,x);
 61     }
 62 }
 63
 64 int t[N];
 65 #define low(x) ((x) & (-(x)))
 66 void upd(int x,int k) {
 67     if(x <= 0) return;
 68     for(int i = x;i <= m;i += low(i)) t[i] += k;
 69 }
 70 int qry(int x) {
 71     if(x <= 0) return 0;
 72     int sum = 0;
 73     for(int i = x;i;i -= low(i)) sum += t[i];
 74     return sum;
 75 }
 76
 77 int ans;
 78 int con[N];
 79 void calc(int x) {
 80     for(int i = head[x];i;i = nxt[i]) {
 81         int sn = mo[i];
 82         if(vis[sn]) continue;
 83         stk[0] = 0,dis[sn] = cst[i],getdis(sn,x);
 84         per(j,stk[0],1) ans += qry(m - stk[j]);
 85         per(j,stk[0],1) {
 86             if(stk[j] > m) continue;
 87             con[++con[0]] = stk[j],upd(stk[j],1);
 88             ans++;
 89         }
 90     }
 91     per(i,con[0],1) upd(con[i],-1);
 92 }
 93
 94 void solve(int x) {
 95     vis[x] = 1,con[0] = 0,calc(x);
 96     for(int i = head[x];i;i = nxt[i]) {
 97         int sn = mo[i];
 98         if(vis[sn]) continue;
 99         sum = sze[sn],maxx[rt = 0] = inf;
100         getroot(sn,sn),solve(rt);
101     }
102 }
103
104 int main() {
105     n = read();
106     rep(i,2,n) add(read(),read());
107     m = read();
108     sum = n,maxx[rt = 0] = inf;
109     getroot(1,1),solve(rt);
110     printf("%d\n",ans);
111     return 0;
112 }

原文地址:https://www.cnblogs.com/yuyanjiaB/p/10052142.html

时间: 2024-07-31 22:27:41

luogu P4178 Tree的相关文章

[Luogu P4178]Tree (点分治+splay)

题面 传送门:https://www.luogu.org/problemnew/show/P4178 Solution 首先,长成这样的题目一定是淀粉质跑不掉了. 考虑到我们不知道K的大小,我们可以开一个splay来统计比某个数小的数的数量. 具体做法等我开淀粉质讲解的坑再满满填(咕) Code #include<iostream> #include<vector> #include<cstdio> using namespace std; long long read

P4178 Tree

最简单的点分治 淀粉质的思想: “分而治之”,缩小问题规模,合并求解: 1 #include<cstdio> 2 #include<cmath> 3 #include<cstring> 4 #include<algorithm> 5 #include<iostream> 6 using namespace std; 7 #define up(i,l,r) for(register int i = (l); i <= (r); ++i) 8

点分治+动态点分治

最近好颓废,什么都学不进去... 感谢两篇:AKMer - 浅谈树分治  言简意赅 LadyLex - 点分治&动态点分治小结  讲解+例题,学到很多东西 点分治 动态点分治 ~ 点分治 ~ 经常遇见一类树上的计数题,问的是在某些条件下,选择一些点的方案数 若对于每个点的统计都需要遍历以其为根节点的子树,普通的做法就是$O(n^2)$的,在很多时候是不满足要求的 而这是点分治的专长 点分治是这样进行的: 1. 找到当前树的重心 2. 将重心及重心连出的边全部删去,那么就能将原来的树分割成森林 3

[luogu P2633] Count on a tree

[luogu P2633] Count on a tree 题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. 输入输出格式 输入格式: 第一行两个整数N,M. 第二行有N个整数,其中第i个整数表示点i的权值. 后面N-1行每行两个整数(x,y),表示点x到点y有一条边. 最后M行每行两个整数(u,v,k),表示一组询问. 输出格式

luogu P3690 【模板】Link Cut Tree (动态树)

https://www.luogu.org/problemnew/show/3690 这大概还是一道模板题目 #include<cstdio> #include<algorithm> const int maxn = 320007; inline int read() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9') { if(c=='-')f=-1;c=getchar(); } while(c<='9'&am

Luogu P4148 简单题(K-D Tree)

题面 题解 因为强制在线,所以我们不能$cdq$分治,所以考虑用$KDT$,$KDT$维护一个矩阵,然后询问的时候如果当前矩形在询问区间内,直接记贡献,否则判断当前点是否在矩阵内,然后左右分别递归下去判断就行了. #include <cstdio> #include <cstring> #include <algorithm> using std::min; using std::max; using std::nth_element; typedef long lon

题解 luogu P1501【[国家集训队]Tree II】(Link-Cut-Tree)

Link-Cut-Tree 的懒标记下传正确食用方法. 我们来逐步分析每一个操作. 1:+ u v c:将u到v的路径上的点的权值都加上自然数c; 解决方法: 很显然,我们可以 split(u,v) 来提取u,v这一段区间,提取完了将 Splay(v),然后直接在v上打加法标记add即可. 代码: inline void pushadd(ll x,ll val){//打标记 s[x]+=sz[x]*val,v[x]+=val,add[x]+=val; s[x]%=MOD,v[x]%=MOD,ad

[lct] Luogu P3690【模板】Link Cut Tree (动态树)

题目描述 给定n个点以及每个点的权值,要你处理接下来的m个操作.操作有4种.操作从0到3编号.点从1到n编号. 0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和.保证x到y是联通的. 1:后接两个整数(x,y),代表连接x到y,若x到y已经联通则无需连接. 2:后接两个整数(x,y),代表删除边(x,y),不保证边(x,y)存在. 3:后接两个整数(x,y),代表将点x上的权值变成y. 输入输出格式 输入格式: 第1行两个整数,分别为n和m,代表点数和操作数. 第2行到第

Luogu P3931 SAC E#1 - 一道难题 Tree 题解

闲扯 为了学习最小割到了这道题,但是看题解的时候发现做法还有树形 \(DP\) 毕竟没怎么认真想过题,写完最小割之后,顺手来了发树形 \(DP\) ,然后就过了,感觉真爽啊~~ 题面 题面 Solution 解法一:最小割最大流 因为要求是所有的叶节点都不能到达根结点,而且可以删除边,要求删边的最小代价,看着就很像最小割.但是仔细一看,发现割完之后叶节点组成了很多个点集,怎么办? 我们只需要再虚拟一个超级汇点,将所有的根结点全部连向该节点,且边的流量均为 \(INF\) .(因为这些边是我们人为