SPOJ COT2 树上找路径上不同值的个数

题目大意

给出多个询问u , v , 求出u-v路径上点权值不同的个数

开始做的是COT1,用主席树写过了,理解起来不难

很高兴的跑去做第二道,完全跟普通数组区间求k个不同有很大区别,完全没思路

膜拜http://www.cnblogs.com/oyking/p/4265823.html

这里利用莫队思想来做,在树上分块,尽可能让相连部分作为一个联通块,那么就在dfs过程中加个手写栈,如果回溯上来的时候保存的值的个数超过每块中应有的

个数那么就将他们分到同一个id块

排序也是跟普通莫队上一样,按分块编号排序,这里有两个端点,那么就先将编号小的摆在前面在排序

离线排序做好了,剩下就是转移的问题,从当前一条路径转移到下一条,diff保存了之前记录的路径上不同点的个数

之前是u,v , 现在去curu , curv , 那么先找到u-curu路径上的点,添加进来,v-curv路径上的点添加进来

画个图可以看出这是原来的路径基础上不需要的点会被再多访问一次,那些需要的点要么本身在原基础上不再访问,要么又多访问两次保持不变,像异或一样

所以记录当前点有没有被访问奇数次就行了,多访问就翻转

最后会有多余的点没有被访问到,就是LCA(u,v) LCA(curu , curv) , 这个要在访问一次即可

这里因为是分块的,所以跑的是曼哈顿距离的询问区间,不会超时

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3
  4 #define N 40010
  5 int first[N] , k;
  6 int block[N] , sz;//对应分到的块的编号和每块应含的大小
  7 int _stack[N] , top , cursz , ID;//手写栈
  8 int n , m;
  9 int hash[N] , a[N] , b[N];
 10
 11 struct Edge{
 12     int x , y , next;
 13     Edge(){}
 14     Edge(int x , int y , int next):x(x),y(y),next(next){}
 15 }e[N<<1];
 16
 17 void add_edge(int x , int y)
 18 {
 19     e[k] = Edge(x , y , first[x]);
 20     first[x] = k++;
 21 }
 22
 23 int dp[N<<1][30] , id[N<<1] , dep[N<<1] , No[N] , fa[N] , dfs_clock;
 24 int depth[N];
 25
 26 void add_block(int &cursz , int ID)
 27 {
 28     while(cursz){
 29         block[_stack[--top]] = ID;
 30        // cout<<"IN: "<<ID<<" "<<_stack[top]<<" "<<top<<" "<<sz<<endl;
 31         cursz--;
 32     }
 33 }
 34
 35 void dfs(int u , int f , int d)
 36 {
 37     id[++dfs_clock] = u , No[u] = dfs_clock , dep[dfs_clock] = d;
 38     fa[u] = f , depth[u] = d;
 39     for(int i=first[u] ; ~i ; i=e[i].next){
 40         int v = e[i].y;
 41         if(v == f) continue;
 42         dfs(v , u , d+1);
 43         id[++dfs_clock] = u , dep[dfs_clock] = d;
 44     }
 45     //树上分块重要部分
 46     cursz++;
 47     _stack[top++] = u;
 48     if(cursz>=sz) add_block(cursz , ++ID);
 49 }
 50
 51 void ST(int n)
 52 {
 53     for(int i=1 ; i<=n ; i++) dp[i][0] = i;
 54     for(int j=1 ; (1<<j)<=n ; j++){
 55         for(int i=1 ; i+(1<<j)-1<=n ; i++){
 56             int a = dp[i][j-1] , b=dp[i+(1<<(j-1))][j-1];
 57             dp[i][j] = dep[a]<dep[b]?a:b;
 58         }
 59     }
 60 }
 61
 62 int RMQ(int l , int r)
 63 {
 64     int k=0;
 65     while((1<<(k+1))<=r-l+1) k++;
 66     int a = dp[l][k] , b = dp[r-(1<<k)+1][k];
 67     return dep[a]<dep[b]?a:b;
 68 }
 69
 70 int LCA(int u , int v)
 71 {
 72     int x=No[u] , y=No[v];
 73     if(x>y) swap(x , y);
 74     return id[RMQ(x,y)];
 75 }
 76
 77 void get_hash(int n){
 78     for(int i=1 ; i<=n ; i++)
 79         hash[i] = lower_bound(b+1 , b+n+1 , a[i])-b;
 80 }
 81
 82 struct Query{
 83     int u , v , id;
 84     void reset(){
 85         if(block[u]>block[v]) swap(u , v);
 86     }
 87     bool operator<(const Query &m) const{
 88         return block[u]<block[m.u]||(block[u]==block[m.u] && block[v]<block[m.v]);
 89     }
 90     void in(int i){scanf("%d%d" , &u , &v);id=i;}
 91 }qu[100010];
 92
 93 int ans[100010] , vis[N] , cnt[N] , diff;
 94
 95 void xorNode(int x)
 96 {
 97    // cout<<"xor: "<<x<<endl;
 98     if(vis[x]) vis[x]=false , diff -= (--cnt[hash[x]]==0);
 99     else vis[x] = true , diff += (++cnt[hash[x]]==1);
100 }
101
102 void xorPath(int x , int y)
103 {
104    // cout<<"path: "<<x<<" "<<y<<endl;
105     if(depth[x]<depth[y]) swap(x , y);
106     while(depth[x]>depth[y]){
107         xorNode(x);
108         x = fa[x];
109     }
110     while(x!=y){
111         xorNode(x) , xorNode(y);
112         x = fa[x] , y=fa[y];
113     }
114 }
115
116 void debug()
117 {
118   //  for(int i=1 ; i<=n ; i++) cout<<"i: "<<block[i]<<" "<<hash[i]<<" fa: "<<fa[i]<<" "<<depth[i]<<endl;
119     cout<<"test: "<<LCA(7,8)<<" "<<LCA(7 , 1)<<endl;
120 }
121 void solve()
122 {
123     sz = (int)sqrt(n+0.5);
124     dfs(1 , 0 , 1);
125     ST(n*2-1);
126     add_block(cursz , ++ID);
127   //  debug();
128     for(int i=0 ; i<m ; i++){
129         qu[i].in(i);
130         qu[i].reset();
131     }
132     sort(qu , qu+m);
133     memset(vis , 0 , sizeof(vis));
134     diff = 0;
135     int curu=1 , curv=1;
136     xorNode(1);
137     for(int i=0 ; i<m ; i++){
138         xorPath(curu , qu[i].u);
139         xorPath(curv , qu[i].v);
140         xorNode(LCA(curu , curv));
141         xorNode(LCA(qu[i].u , qu[i].v));
142         curu = qu[i].u , curv = qu[i].v;
143         ans[qu[i].id] = diff;
144        // cout<<"----华丽的分割线---"<<endl;
145     }
146     for(int i=0 ; i<m ; i++) printf("%d\n" , ans[i]);
147 }
148
149 int main()
150 {
151   //  freopen("in.txt" , "r" , stdin);
152     scanf("%d%d" , &n , &m);
153     for(int i=1 ; i<=n ; i++) scanf("%d" , &a[i]) , b[i]=a[i];
154     sort(b+1 , b+n+1);
155     get_hash(n);
156     int x , y;
157     memset(first , -1 , sizeof(first));
158     top = cursz = ID = k = 0;
159     for(int i=1 ; i<n ; i++){
160         scanf("%d%d" , &x , &y);
161         add_edge(x , y);
162         add_edge(y , x);
163     }
164     solve();
165     return 0;
166 }
时间: 2024-10-12 18:55:17

SPOJ COT2 树上找路径上不同值的个数的相关文章

poj 3417 Network (LCA,路径上有值)

题意: N个点,构成一棵树.给出这棵树的结构. M条边,(a1,b1)...(am,bm),代表给树的这些点对连上边.这样就形成了有很多环的一个新”树“. 现在要求你在原树中断一条边,在M条边中断一条边,使得新”树“被分成两个部分. 问有多少种方案. 思路: 连上某条新边(a,b),则必定形成一个环.环的路径是a->...->lca(a,b)->...->b,给这些边的值都加1 . 分情况: 在原树中,若一条边的值>=2,去掉这条边,再去掉M条边中的一条,不影响原树的连通性.

[spoj COT2]树上莫队

题目链接:http://www.spoj.com/problems/COT2/ 学会了树上莫队,真的是太激动了!参照博客:http://codeforces.com/blog/entry/43230 讲的十分清楚. #include<bits/stdc++.h> using namespace std; const int MAXN=40005; const int maxm=100005; int cur; int sat[MAXN]; int ean[MAXN]; int A[MAXN*2

输出二叉树中路径上结点值之和为给定值的所有路径

#include<iostream>#include<vector>#include<algorithm>#include<stdint.h>using namespace std;#include<list>#include<map>#include<queue>struct node{ int val; node* left, *right; node(int _val) :val(_val), left(NULL),

SPOJ COT2 Count on a tree II (树上莫队,倍增算法求LCA)

题意:给一个树图,每个点的点权(比如颜色编号),m个询问,每个询问是一个区间[a,b],图中两点之间唯一路径上有多少个不同点权(即多少种颜色).n<40000,m<100000. 思路:无意中看到树上莫队,只是拿来练练,没有想到这题的难点不在于树上莫队,而是判断LCA是否在两点之间的路径上的问题.耗时1天. 树上莫队的搞法就是: (1)DFS一次,对树进行分块,分成sqrt(n)块,每个点属于一个块.并记录每个点的DFS序. (2)将m个询问区间用所属块号作为第一关键字,DFS序作为第二关键字

CF E2 - Daleks&#39; Invasion (medium) (LCA求两点树上路径上的最大边权)

http://codeforces.com/contest/1184/problem/E2 题意:给出一副图,首先求出这幅图的最小生成树 , 然后修改这幅图上不属于最小生成树的边权,使得修改后的图在求一边生成树的时候可以包含被修改的边(注意:修改的边权要最大 )题目规定只有一课生成树 分析: 现在我们需要解决所有非树边的任务(MST保证是惟一的).我们要求对于非树边(u, v),正确答案是u和v之间路径上的最大权值MST.(证明:≤:由MSTs的循环特性可知;≥:如果(u, v)的重量大于这个最

poj 2455 Secret Milking Machine 【二分 + 最大流】 【1到N不重复路径不少于T条时,求被选中路径上的最大边权值 的最小值】

Secret Milking Machine Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 10620   Accepted: 3110 Description Farmer John is constructing a new milking machine and wishes to keep it secret as long as possible. He has hidden in it deep within

bzoj 3784: 树上的路径 堆维护第k大

3784: 树上的路径 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 88  Solved: 27[Submit][Status][Discuss] Description 给定一个N个结点的树,结点用正整数1..N编号.每条边有一个正整数权值.用d(a,b)表示从结点a到结点b路边上经过边的权值.其中要求a<b.将这n*(n-1)/2个距离从大到小排序,输出前M个距离值. Input 第一行两个正整数N,M 下面N-1行,每行三个正整数a,b,

【BZOJ3784】树上的路径 点分治序+ST表

[BZOJ3784]树上的路径 Description 给定一个N个结点的树,结点用正整数1..N编号.每条边有一个正整数权值.用d(a,b)表示从结点a到结点b路边上经过边的权值.其中要求a<b.将这n*(n-1)/2个距离从大到小排序,输出前M个距离值. Input 第一行两个正整数N,M 下面N-1行,每行三个正整数a,b,c(a,b<=N,C<=10000).表示结点a到结点b有一条权值为c的边. Output 共M行,如题所述. Sample Input 5 10 1 2 1

【BZOJ-3784】树上的路径 点分治 + ST + 堆

3784: 树上的路径 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 462  Solved: 153[Submit][Status][Discuss] Description 给定一个N个结点的树,结点用正整数1..N编号.每条边有一个正整数权值.用d(a,b)表示从结点a到结点b路边上经过边的权值.其中要求a<b.将这n*(n-1)/2个距离从大到小排序,输出前M个距离值. Input 第一行两个正整数N,M 下面N-1行,每行三个正整数a,