bzoj 3697

终于自己独立A了一道点分治。

点分治的题目和树规的题目很像,都是在树上乱搞,但点分治一般和路径更相关,如果用树规做会感觉冗余状态太多,内存和时间都无法承受,如果不用动态规划,直接在原树上运用这道题的方法,又无法保证时间复杂度(点分治让我们的算法对原树的形态依赖更小,可以保证时间复杂度)。

  1 /**************************************************************
  2     Problem: 3697
  3     User: idy002
  4     Language: C++
  5     Result: Accepted
  6     Time:2480 ms
  7     Memory:14012 kb
  8 ****************************************************************/
  9
 10 #include <cstdio>
 11 #define N 100010
 12 #define fprintf(...)
 13
 14 int n;
 15 int head[N], dest[N+N], wght[N+N], next[N+N], ntot;
 16 int fat[N], vis[N], siz[N], bac[N], dis[N], cur_root, cur_siz;
 17 int vv[N+N], svv[N+N][2], cvv[N+N][2];
 18 int stkc[N+N], tpc, stks[N+N], tps;
 19 long long ans;
 20
 21 void insert( int u, int v, int w ) {
 22     ntot++;
 23     next[ntot] = head[u];
 24     wght[ntot] = w ? 1 : -1;
 25     dest[ntot] = v;
 26     head[u] = ntot;
 27 }
 28 void dfs_root( int u ) {
 29     siz[u] = 1, bac[u] = 0;
 30     for( int t=head[u]; t; t=next[t] ) {
 31         int v=dest[t];
 32         if( vis[v] || v==fat[u] ) continue;
 33         fat[v] = u;
 34         dfs_root(v);
 35         siz[u] += siz[v];
 36         if( siz[v]>bac[u] ) bac[u]=siz[v];
 37     }
 38     if( cur_siz-siz[u]>bac[u] ) bac[u]=cur_siz-siz[u];
 39     if( bac[u]<bac[cur_root] ) cur_root=u;
 40 }
 41 void dfs_dis( int u ) {
 42     if( dis[u]==0 && vv[N+0]>1 )
 43         ans++;
 44     bool ok = vv[N+dis[u]];
 45     if( ok ) {
 46         ans += svv[N-dis[u]][0]+svv[N-dis[u]][1];
 47         cvv[N+dis[u]][1]++;
 48     } else {
 49         ans += svv[N-dis[u]][1];
 50         cvv[N+dis[u]][0]++;
 51     }
 52     stkc[++tpc] = dis[u];
 53     fprintf( stderr, "Arrived %d cur_ans = %lld\n", u, ans );
 54     vv[N+dis[u]]++;
 55     for( int t=head[u]; t; t=next[t] ) {
 56         int v=dest[t], w=wght[t];
 57         if( vis[v] || v==fat[u] ) continue;
 58         dis[v] = dis[u]+w;
 59         fat[v] = u;
 60         dfs_dis( v );
 61     }
 62     vv[N+dis[u]]--;
 63 }
 64 void build_vdcp( int rt ) {
 65     /*   found the core   */
 66     cur_siz = siz[rt];
 67     cur_root = 0;
 68     fat[rt] = rt;
 69     dfs_root( rt );
 70     /*   collect the info  */
 71     rt = cur_root;
 72     vis[rt] = true;
 73     fprintf( stderr, "cur_root = %d\n", rt );
 74     for( int t=head[rt]; t; t=next[t] ) {
 75         int u=dest[t], w=wght[t];
 76         if( vis[u] ) continue;
 77         tpc = 0;
 78         vv[N+0]++;
 79         fat[u] = rt;
 80         dis[u] = w;
 81         dfs_dis( u );
 82         vv[N+0]--;
 83         while( tpc ) {
 84             int v = stkc[tpc--];
 85             if( !cvv[N+v][0] && !cvv[N+v][1] ) continue;
 86             svv[N+v][0] += cvv[N+v][0];
 87             svv[N+v][1] += cvv[N+v][1];
 88             stks[++tps] = v;
 89             cvv[N+v][0] = cvv[N+v][1] = 0;
 90         }
 91     }
 92     while( tps ) {
 93         int v = stks[tps--];
 94         svv[N+v][0] = svv[N+v][1] = 0;
 95     }
 96     for( int t=head[rt]; t; t=next[t] ) {
 97         int u=dest[t];
 98         if( vis[u] ) continue;
 99         build_vdcp( u );
100     }
101 }
102 int main() {
103     scanf( "%d", &n );
104     for( int i=1,u,v,w; i<n; i++ ) {
105         scanf( "%d%d%d", &u, &v, &w );
106         insert( u, v, w );
107         insert( v, u, w );
108     }
109     bac[0] = n;
110     siz[1] = n;
111     build_vdcp(1);
112     printf( "%lld\n", ans );
113 }

时间: 2024-11-08 10:23:48

bzoj 3697的相关文章

【BZOJ 3697】采药人的路径

题目链接: TP 题解: 调了好久233. 大概想一想就是树分,然后考虑这样路径(u,v)的特征,以根节点(root)切开,u到root的阴阳差值,和v到root巧合互为相反数,然后考虑要有一个点可作为休息点,即u/v到root的路径中要有一点x与u/v到root的阴阳差值相同,然后维护一下就好. 注意的是阴阳差为0的特判……写挂了调好久,对拍也不好写,真是恶心. 代码: 1 #define Troy 11/23 2 #define inf 0x7fffffff 3 4 #include <bi

【BZOJ】【3697】采药人的路径 &amp; 【3127】【USACO2013 Open】Yin and Yang

点分治 Orz hzwer 倒是比较好想到点分治……然而在方案统计这里,我犯了两个错误…… 1.我比较傻逼的想的是:通过儿子来更新父亲,也就是统计以x为根的子树中xxxx的路径有多少条……这样转移. 然而这实在是太傻逼了,黄学长教做人:从父亲来更新儿子,走到一个节点直接更新路径的统计数,反正我们要的是[经过root的xx路径的数量] 所以可以一遍dfs直接搞出来…… 2.统计方案的方式也想错了……我只考虑了以root作为中转站的路径,然而经过root的路径中,并不只有这种路径是合法的……中转站在

NOI前总结:点分治

点分治: 点分治的题目基本一样,都是路径计数. 其复杂度的保证是依靠 $O(n)$ 找重心的,每一次至少将问题规模减小为原先的$1/2$. 找重心我喜欢$BFS$防止爆栈. 1 int Root(int x){ 2 dfsn[0]=0; 3 q.push(x); fa[x]=0; 4 flag[x]=1; 5 while(!q.empty()){ 6 int x=q.front(); q.pop(); 7 dfsn[++dfsn[0]]=x; 8 for(int i=g[x];i;i=E[i].

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取模,将所得答案插入到数列

【BZOJ】【1016】【JSOI2008】最小生成树计数

Kruskal/并查集+枚举 唉我还是too naive,orz Hzwer 一开始我是想:最小生成树删掉一条边,再加上一条边仍是最小生成树,那么这两条边权值必须相等,但我也可以去掉两条权值为1和3的,再加上权值为2和2的,不也满足题意吗?事实上,如果这样的话……最小生成树应该是1和2,而不是1和3或2和2!!! 所以呢?所以对于一个图来说,最小生成树有几条边权为多少的边,都是固定的!所以我们可以做一遍Kruskal找出这些边权,以及每种边权出现的次数.然后,对于每种边权,比方说出现了$v_i$