bzoj 3772

感觉做这种题收获很大。

1、DFS序(广义上)除了用于静态子树操作,也可以用来做点到根的路上某些信息的统计(如点到根的路径上标记了多少个点),如果在加上lca,就可以支持路径的信息查询。

2、树上的可持久化线段树,如果每个节点要维护一个线段树,并且该线段树支持加减操作,那么通过可持久化+lca,搞定一条路径上的线段树的和(当然不仅局限于线段树)。

3、一条树上的路径覆盖另一条路径 <=> 后者的两个端点在前者的路径上。

题解看PoPoQQQ的博客:

http://blog.csdn.net/popoqqq/article/details/43122821

如果不清楚就看看代码。

  1 /**************************************************************
  2     Problem: 3772
  3     User: idy002
  4     Language: C++
  5     Result: Accepted
  6     Time:5824 ms
  7     Memory:65180 kb
  8 ****************************************************************/
  9
 10 #include <cstdio>
 11 #include <vector>
 12 #define N 100010
 13 #define S 4000000
 14 #define P 16
 15 using namespace std;
 16
 17 typedef long long dnt;
 18
 19 struct Node {
 20     int s;
 21     Node *ls, *rs;
 22 }pool[S], *tail=pool, *root[N], *null=pool;
 23 struct Qry {
 24     int u, v;
 25     Qry(){}
 26     Qry( int u, int v ):u(u),v(v){}
 27 };
 28
 29 int n, m;
 30 int head[N], dest[N+N], next[N+N], ntot;
 31 int in[N], out[N], idc;
 32 int anc[N][P+1], dep[N];
 33 vector<int> vc[N];
 34 Qry qry[N];
 35
 36 void insert( int u, int v ) {
 37     ntot++;
 38     next[ntot] = head[u];
 39     dest[ntot] = v;
 40     head[u] = ntot;
 41 }
 42 Node *modify( Node *nd, int lf, int rg, int pos, int delta ) {
 43     Node *rt = ++tail;
 44     if( lf==rg ) {
 45         rt->s = nd->s + delta;
 46         return rt;
 47     }
 48     int mid=(lf+rg)>>1;
 49     if( pos<=mid ) {
 50         rt->ls = modify( nd->ls, lf, mid, pos, delta );
 51         rt->rs = nd->rs;
 52     } else {
 53         rt->ls = nd->ls;
 54         rt->rs = modify( nd->rs, mid+1, rg, pos, delta );
 55     }
 56     rt->s = rt->ls->s + rt->rs->s;
 57     return rt;
 58 }
 59 int query( Node *nd, int lf, int rg, int L, int R ) {
 60     if( L<=lf && rg<=R ) return nd->s;
 61     int mid=(lf+rg)>>1;
 62     int rt = 0;
 63     if( L<=mid ) rt += query( nd->ls, lf, mid, L, R );
 64     if( R>mid ) rt += query( nd->rs, mid+1, rg, L, R );
 65     return rt;
 66 }
 67 int query( Node *p1, Node *p2, Node *p3, Node *p4, int L, int R ) {
 68     /*  (p1+p2-p3-p4) as one tree  */
 69     int s1, s2, s3, s4;
 70     s1 = query(p1,1,idc,L,R);
 71     s2 = query(p2,1,idc,L,R);
 72     s3 = query(p3,1,idc,L,R);
 73     s4 = query(p4,1,idc,L,R);
 74     return s1+s2-s3-s4;
 75 }
 76 void dfs1( int u ) {
 77     in[u] = ++idc;
 78     for( int p=1; p<=P; p++ )
 79         anc[u][p] = anc[anc[u][p-1]][p-1];
 80     for( int t=head[u]; t; t=next[t] ) {
 81         int v=dest[t];
 82         if( v==anc[u][0] ) continue;
 83         anc[v][0] = u;
 84         dep[v] = dep[u]+1;
 85         dfs1(v);
 86     }
 87     out[u] = ++idc;
 88 }
 89 void dfs2( int u ) {
 90     root[u] = root[anc[u][0]];
 91     for( int t=0; t<vc[u].size(); t++ ) {
 92         int v=vc[u][t];
 93         root[u] = modify( root[u], 1, idc, in[v], +1 );
 94         root[u] = modify( root[u], 1, idc, out[v], -1 );
 95     }
 96     for( int t=head[u]; t; t=next[t] ) {
 97         int v=dest[t];
 98         if( v==anc[u][0] ) continue;
 99         dfs2(v);
100     }
101 }
102 int lca( int u, int v ) {
103     if( dep[u]<dep[v] ) swap(u,v);
104     int t=dep[u]-dep[v];
105     for( int p=0; t; t>>=1,p++ )
106         if( t&1 ) u=anc[u][p];
107     if( u==v ) return u;
108     for( int p=P; p>=0 && anc[u][0]!=anc[v][0]; p-- )
109         if( anc[u][p]!=anc[v][p] )
110             u=anc[u][p], v=anc[v][p];
111     return anc[u][0];
112 }
113 dnt gcd( dnt a, dnt b ) {
114     return b ? gcd(b,a%b) : a;
115 }
116 int main() {
117     scanf( "%d%d", &n, &m );
118     for( int i=1,u,v; i<n; i++ ) {
119         scanf( "%d%d", &u, &v );
120         insert( u, v );
121         insert( v, u );
122     }
123     for( int i=1,u,v; i<=m; i++ ) {
124         scanf( "%d%d", &u, &v );
125         vc[u].push_back(v);
126         qry[i] = Qry(u,v);
127     }
128
129     anc[1][0] = 0;
130     dep[1] = 1;
131     dfs1(1);
132
133     null->ls = null->rs = null;
134     root[0] = null;
135     dfs2(1);
136
137     dnt cnt = 0, tot = 0, cd = 0;
138     for( int i=1; i<=m; i++ ) {
139         int u=qry[i].u, v=qry[i].v, ca=lca(u,v);
140         cnt += query( root[u], root[v], root[ca], root[anc[ca][0]], in[ca], in[u] );
141         cnt += query( root[u], root[v], root[ca], root[anc[ca][0]], in[ca], in[v] );
142         cnt -= query( root[u], root[v], root[ca], root[anc[ca][0]], in[ca], in[ca] );
143         cnt --;
144     }
145     tot = (dnt)m*(m-1)/2;
146     cd = gcd(tot,cnt);
147     printf( "%lld/%lld\n", cnt/cd, tot/cd );
148 }

时间: 2024-09-30 05:20:39

bzoj 3772的相关文章

bzoj 3772 :精神污染 线段树+打标记 or 主席树

3772: 精神污染 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 315  Solved: 87[Submit][Status][Discuss] Description 兵库县位于日本列岛的中央位置,北临日本海,南面濑户内海直通太平洋,中央部位是森林和山地,与拥有关西机场的大阪府比邻而居,是关西地区面积最大的县,是集经济和文化于一体的一大地区,是日本西部门户,海陆空交通设施发达.濑户内海沿岸气候温暖,多晴天,有日本少见的贸易良港神户港所在的神户

【BZOJ 3772】精神污染 主席树+欧拉序

这道题的内存-------真·精神污染---.. 这道题的思路很明了,我们就是要找每一个路径包含了多少其他路径那么就是找,有多少路径的左右端点都在这条路径上,对于每一条路径,我们随便选定一个端点作为第一关键字,另一个作为第二关键字,于是就有了两维限制,按照主席树的一般思路,我们把建树顺序作为一维,然后在里面维护另一维,那么我们在外面限制第一关键字,就是在树上建主席树,查询减LCA,在里面的话我们把每个点作为第一关键字对应的第二关键字,放入主席树,而主席树维护的是欧拉序区间,所以我们每次查询只用查

bzoj 3772 精神污染 主席树+dfs序

精神污染 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 637  Solved: 177[Submit][Status][Discuss] Description 兵库县位于日本列岛的中央位置,北临日本海,南面濑户内海直通太平洋,中央部位是森林和山地,与拥有关西机场的大阪府比邻而居,是关西地区面积最大的县,是集经济和文化于一体的一大地区,是日本西部门户,海陆空交通设施发达.濑户内海沿岸气候温暖,多晴天,有日本少见的贸易良港神户港所在的神户市和曾是豪

3.22 模拟赛

T1 sort 题目大意: 思路: T2 mission 题解链接 考场的$dsu \space on \space tree$被卡掉了,写一波主席树合并 1 #include<bits/stdc++.h> 2 #define ll long long 3 #define db double 4 #define inf 2139092143 5 #define MAXN 10100 6 #define DMAXN 640100 7 #define DMAXM 1280100 8 #define

BZOJ 1013: [JSOI2008]球形空间产生器sphere

二次联通门 : BZOJ 1013: [JSOI2008]球形空间产生器sphere /* BZOJ 1013: [JSOI2008]球形空间产生器sphere 高斯消元 QAQ SB的我也能终于能秒题了啊 设球心的坐标为(x,y,z...) 那么就可以列n+1个方程,化化式子高斯消元即可 */ #include <cstdio> #include <iostream> #include <cstring> #define rg register #define Max

bzoj 3309 DZY Loves Math - 莫比乌斯反演 - 线性筛

对于正整数n,定义f(n)为n所含质因子的最大幂指数.例如f(1960)=f(2^3 * 5^1 * 7^2)=3, f(10007)=1, f(1)=0. 给定正整数a,b,求sigma(sigma(f(gcd(i,j)))) (i=1..a, j=1..b). Input 第一行一个数T,表示询问数. 接下来T行,每行两个数a,b,表示一个询问. Output 对于每一个询问,输出一行一个非负整数作为回答. Sample Input 4 7558588 9653114 6514903 445

【BZOJ】[HNOI2009]有趣的数列

[算法]Catalan数 [题解] 学了卡特兰数就会啦>_<! 因为奇偶各自递增,所以确定了奇偶各自的数字后排列唯一. 那么就是给2n个数分奇偶了,是不是有点像入栈出栈序呢. 将做偶数标为-1,做奇数标为+1,显然当偶数多于奇数时不合法,因为它压不住后面的奇数. 然后其实这种题目,打表就可知啦--QAQ 然后问题就是求1/(n+1)*C(2n,n)%p了,p不一定是素数. 参考bzoj礼物的解法. 看到网上清一色的素数筛+分解质因数解法,不解了好久,感觉写了假的礼物-- 后来觉得礼物的做法才比

洛谷 P2709 BZOJ 3781 小B的询问

题目描述 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数.小B请你帮助他回答询问. 输入输出格式 输入格式: 第一行,三个整数N.M.K. 第二行,N个整数,表示小B的序列. 接下来的M行,每行两个整数L.R. 输出格式: M行,每行一个整数,其中第i行的整数表示第i个询问的答案. 输入输出样例 输入样例#1: 6 4 3 1 3 2 1 1 3

BZOJ 1012: [JSOI2008]最大数maxnumber(线段树)

012: [JSOI2008]最大数maxnumber Time Limit: 3 Sec  Memory Limit: 162 MB Description 现在请求你维护一个数列,要求提供以下两种操作:1. 查询操作.语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值.限制:L不超过当前数列的长度.2. 插入操作.语法:A n 功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列