[luogu4556][Vani有约会]

题目链接

吐槽

这道题调了7个小时也是够了。最后只好比着题解做了一遍2333

思路

首先考虑n=2000的情况。因为这是在一条路径上,所以可以考虑差分。用a[i][j]表示第i个点中j这种粮食出现的次数。加入要在从x到y的路径上加入c这种粮食。将这条路径分为两部分进行差分。从x到lca,也就是将a[x][c]++,a[fa[lca]]--。从y到son[lca]。就是将a[y][c]++,a[lca]--。(详细原因见树上差分)最后dfs一遍,并且在回溯的时候合并一下就行了。
那么对于n=100000的数据,只要用线段树合并优化一下就行了。也就是将a数组改为n棵动态开点线段树。合并的时候用线段树合并的板子合并一下就行了。

代码

/*
* @Author: wxyww
* @Date:   2018-12-10 14:37:11
* @Last Modified time: 2018-12-10 21:15:06
*/
#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
#define Ls TR[cur].ls
#define Rs TR[cur].rs
ll read() {
   ll x = 0,f = 1;char c = getchar();
   while(c < '0' || c > '9') {
      if(c == '-') f = -1;
      c = getchar();
   }
   while(c >= '0' && c <= '9') {
      x = x * 10 + c - '0';
      c = getchar();
   }
   return x * f;
}
const int N = 100000 + 100,logN = 20;
struct node {
   int ls,rs,ans,sum;
}TR[N * 50];
int n,m,dep[N],lca[N][logN + 30],root[N];
vector<int>e[N];
int num;
void get_lca(int u,int father) {
   for(int i = 1;i < logN;++i) lca[u][i] = lca[lca[u][i - 1]][i - 1];
   int k = e[u].size();
   for(int i = 0;i < k;++i) {
      int v = e[u][i];
      if(v == father) continue;
      lca[v][0] = u;
      dep[v] = dep[u] + 1;
      get_lca(v,u);
   }
}
int LCA(int x,int y) {
   if(dep[x] < dep[y]) swap(x,y);
   for(int i = logN - 1;i >= 0;--i)
      if(dep[lca[x][i]] >= dep[y]) x = lca[x][i];
   if(x == y) return y;
   for(int i = logN - 1;i >= 0;--i) {
      if(lca[x][i] != lca[y][i]) {
         x = lca[x][i];y = lca[y][i];
      }
   }
   return lca[x][0];
}
void up(int cur) {
   if(TR[Ls].sum >= TR[Rs].sum) {
      TR[cur].sum = TR[Ls].sum;
      TR[cur].ans = TR[Ls].ans;
   }
   else {
      TR[cur].sum = TR[Rs].sum;
      TR[cur].ans = TR[Rs].ans;
   }
}
void update(int &cur,int l,int r,int pos,int c) {
   if(!cur) cur = ++num;
   if(l == r) {
      TR[cur].sum += c;
      TR[cur].ans = l;
      return;
   }
   int mid = (l + r) >> 1;
   if(pos <= mid) update(Ls,l,mid,pos,c);
   else update(Rs,mid + 1,r,pos,c);
   up(cur);
}
int merge(int cur,int a,int l,int r) {
   if(!cur) return a;
   if(!a) return cur;
   if(l == r) {
      TR[cur].sum += TR[a].sum;
      TR[cur].ans = l;
      return cur;
   }
   int mid = (l + r) >> 1;
   Ls = merge(Ls,TR[a].ls,l,mid);
   Rs = merge(Rs,TR[a].rs,mid + 1,r);
   up(cur);
   return cur;
}
void dfs(int u,int father) {
   int k = e[u].size();
   for(int i = 0;i < k;++i) {
      int v = e[u][i];
      if(v == father) continue;
      dfs(v,u);
      merge(root[u],root[v],1,N - 100);
   }
}
int main() {
   int n = read(),m = read();
   for(int i = 1;i < n;++i) {
      int x = read(),y = read();
      e[x].push_back(y);
      e[y].push_back(x);
   }
   dep[1] = 1;
   for(int i = 1;i <= n;++i) root[i] = ++num;
   get_lca(1,0);
   while(m--) {
      int x = read(),y = read(),c = read();
      int K = LCA(x,y);
      update(root[x],1,N - 100,c,1);
      update(root[y],1,N - 100,c,1);
      update(root[K],1,N - 100,c,-1);
      if(lca[K][0]) update(root[lca[K][0]],1,N - 100,c,-1);
   }
   dfs(1,0);
   for(int i = 1;i <= n;++i) {
      if(TR[root[i]].sum == 0) puts("0");
      else
      printf("%d\n",TR[root[i]].ans);
   }

   return 0;
}
全部评论

相关推荐

12-01 12:34
已编辑
广东工业大学 Java
如题,fw🐭🐭,加上准备的太晚,大三上已找不到日常实习,导致连锁反应,下学期的暑期实习找不到好的实习,导致秋招找不到中大厂,现在是中小厂Java还有考公的选择,由于有些中小厂工作强度比肩大厂,钱还少,感觉不如考公如果🐮u们是我现在这种情况,会怎么选?
负债的混子:关注你一段时间了,突然发现你头像名字都改了,想必是这段时间压力很大。关于就业还是考公的选择,就像很多牛友说的:不要美化自己没走过的路。你现在想往互联网发展,发现这条路很难走,然后想往考公发展,但是你没走过考公这条路,所以你不知道这条路的压力如何。你今年大三了,还有时间给你做选择,我希望你能够尽快的决定自己的方向,然后一条路走到黑,而不是在这里徘徊,每个人的道路是不一样的,你无法复刻别人的路,你能做的就是尽力的完善自己。 最后,我想说的是,加油,陌生人!
点赞 评论 收藏
分享
专心打鱼:互联网搬运工,贴子都要偷
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务