BZOJ 3648|寝室管理|点分治|树状数组|平衡树
2016-04-29 09:53
316 查看
题意
给出一个至多有1个环的图,求长度不超过K的路径数。GDOI 2016 Day2 T3原题在此
题解
考虑树的情况,显然用点分治就可以搞出来了。对于过根的路径,先处理出各深度有多少的点,再依次枚举各子树,对于各点,有其深度,问题变成了在其他子树中,与现有深度之和不超过K的有多少个,而枚举的点到根的距离是已知的t,那么就变成在之前的子树中,深度不超过K-t的点有多少个,平衡树、树状数组都可以轻易地维护。O(nlog2n)
环呢?随便删除一条边就变成树了,然后我们再考虑被删除的这条边对答案的贡献,考虑接在环上的所有子树,统计各子树后,我们就只需要扫一遍环。
我们在枚举子树的时候,每当移动一次,到之前扫过的树的距离就少了1(我们必须得经过那条被删除的边),这样看来,之前扫过的每棵树允许的深度都不一样。发现相邻的两棵子树,允许的深度差总是1,因此我们在处理树状数组的时候,把深度+1即可,每扫依次,深度就再+1,然后就可以在O(nlogn)时间内解决了。
比如对于环1-2-3-4-5-6-1,假设切断1-6这条边,从1->6的方向枚举。
K=3。
枚举1,把1插入到bit[1]。我们令环上的点的深度为0。
……
枚举6,考虑之前枚举过的,到达
j=1经过了2个点,那么层数超过
K-dis=1的,都对答案有贡献,而且显然
dis=cir-i+j+1,表示环上过被删除边的距离,i为枚举的点在环中的位置,也就是枚举到第i次的点。到达
j=2经过了3个点,那么层数超过0的都计入,
j=3经过4个点,层数超过-1的都计入……,
j=5经过了6个点,层数超过-3的都计入。
但总不可能一一枚举j,我们发现,对于任意的i,我们枚举的j要统计的层数总是一个公差为-1的等差数列,与数列
{5,4,3,2,1}的距离(这里指对应元素的差)为
ERROR,我们可以利用这个性质。
对于枚举的6,到1~5的层数为
{1,0,-1,-2,-3}(实际上负数和0没有区别)。那么5插入的深度全部+5,4插入的深度全部+4,……,层数就变成了
{2,2,2,2,2}。那么我们在查询BIT的时候,只要查询
>=2的总和就可以了。
对于枚举的5,到1~4的层数为
{0,-1,-2,-3},此时只要查询>=1的总和就可以了。
对于枚举的4,到1~3的层数为
{-1,-2,-3},只要查询>=0的总和即可。
假设4有个孩子(即深度为1),那么到1~3的层数就为
{-2,-3,-4},只要查询>=-1的总和即可。
总结一下,对于枚举的点,查询的即为
>=K-(dis<x,1>-1+level)。level是深度。
dis<x,1>=cir-i+2
直接飞到Rank 4了。。。
不知道怎么优化常数了QwQ
树状数组版本(1.6s)
#include <cstdio> #include <algorithm> using namespace std; #define adj(i,j) for(i=h[j];i;i=p[i]) #define FOR(i,j,k) for(i=j;i<=k;++i) inline int read() { int s=0,f=1;char ch=getchar(); for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1; for(;'0'<=ch&&ch<='9';ch=getchar())s=s*10+ch-'0'; return s*f; } const int N = 200005, M = N * 2, inf = 2147483647; long long ans; int bit , c , cir, n, K, sk , top, no ; int h , p[M], v[M], cnt = 1; int vis , f , rt, sum, sz ; inline void add(int i, int x) { for (; i; i -= i & -i) bit[i] += x; } inline int ask(int i) { if (i < 1) i = 1; int s = 0; for (; i <= n + cir; i += i & -i) s += bit[i]; return s; } void getroot_impl(int x, int fa) { int i; sz[x] = 1; f[x] = 0; adj(i, x) if (!vis[v[i]] && v[i] != fa && !no[i]) { getroot_impl(v[i], x); sz[x] += sz[v[i]]; f[x] = max(f[x], sz[v[i]]); } f[x] = max(f[x], sum - sz[x]); if (f[x] < f[rt]) rt = x; } inline int getroot(int x, int fa, int sz) { sum = sz; f[rt = 0] = inf; getroot_impl(x, fa); return rt; } void dfs_depth(int x, int fa, int d) { int i; sk[++top] = d; adj(i, x) if (v[i] != fa && !vis[v[i]] && !no[i]) dfs_depth(v[i], x, d + 1); } void divide(int x) { int l = 0, i, j; vis[x] = 1; adj(i, x) if (!vis[v[i]] && !no[i]) { l = top; dfs_depth(v[i], x, 1); FOR(j,l+1,top) ans += ask(K - sk[j] - 1); FOR(j,l+1,top) add(sk[j], 1); } ans += ask(K - 1); while (top) add(sk[top--], -1); adj(i, x) if (!vis[v[i]] && !no[i] && sz[v[i]] >= K) divide(getroot(v[i], x, sz[v[i]])); } void dfs_circle(int x, int fa) { if (cir) return; int i; if (vis[x]) { c[cir = 1] = x; for (i = top; sk[i] != x; c[++cir] = sk[i--]); } vis[x] = 1; sk[++top] = x; adj(i, x) if (v[i] != fa) dfs_circle(v[i], x); vis[x] = 0; --top; } inline void work() { int i, j; dfs_circle(1, 0); adj(i, c[1]) if (v[i] == c[cir]) { no[i] = no[i ^ 1] = 1; break; } divide(getroot(1, 0, n)); FOR(i,1,n) vis[i] = 0; FOR(i,1,cir+n) bit[i] = 0; FOR(i,1,cir) vis[c[i]] = 1; FOR(i,1,cir) { vis[c[i]] = 0; dfs_depth(c[i], 0, 0); vis[c[i]] = 1; FOR(j,1,top) ans += ask(K - (cir - i + 1) - sk[j]); while (top) add(sk[top--] + i, 1); } } int main() { int m, a, b, i; n = read(); m = read(); K = read(); FOR(i,1,m) { a = read(); b = read(); p[++cnt] = h[a]; v[cnt] = b; h[a] = cnt; p[++cnt] = h[b]; v[cnt] = a; h[b] = cnt; } if (n == m) work(); else divide(getroot(1, 0, n)); printf("%lld", ans); return 0; }
平衡树版本(21s)
#include <cstdio> #include <algorithm> using namespace std; #define adj(i,j) for(int i=h[j];i;i=p[i]) #define FOR(i,j,k) for(int i=j;i<=k;++i) const int N = 200005, M = N * 2, inf = 2147483647; long long ans; int bit , c , cir, n, K, sk , top, no ; int h , p[M], v[M], cnt = 1; int vis , f , rt, sum, sz ; void edge(int a, int b) { p[++cnt] = h[a]; v[cnt] = b; h[a] = cnt; } namespace SBT { int cnt, root; struct Node { int l, r, s, key; } tree ; #define l(x) tree[x].l #define r(x) tree[x].r #define s(x) tree[x].s #define key(x) tree[x].key void reset() { cnt = root = 0; } void r_rotate(int &t) { int k = l(t); l(t) = r(k); r(k) = t; s(k) = s(t); s(t) = s(l(t)) + s(r(t)) + 1; t = k; } void l_rotate(int &t) { int k = r(t); r(t) = l(k); l(k) = t; s(k) = s(t); s(t) = s(l(t)) + s(r(t)) + 1; t = k; } void maintain(int &t, bool flag) { if (!flag) { if(s(l(l(t))) > s(r(t))) r_rotate(t); else if(s(r(l(t))) > s(r(t))) { l_rotate(l(t)); r_rotate(t); } else return; } else { if(s(r(r(t))) > s(l(t))) l_rotate(t); else if(s(l(r(t))) > s(l(t))) { r_rotate(r(t)); l_rotate(t); } else return; } maintain(l(t), false); maintain(r(t), true); maintain(t, false); maintain(t, true); } void insert(int &t, int v, bool flag = 1) { if (!t) { t = ++cnt; key(t) = v; l(t) = r(t) = 0; s(t) = 1; } else { ++s(t); insert(v < key(t) ? l(t) : r(t), v, 0); } if (flag) maintain(t, v >= key(t)); } int greaterthan(int t, int w) { if (!t) return 0; if (key(t) >= w) return s(r(t)) + 1 + greaterthan(l(t), w); else return greaterthan(r(t), w); } } void getroot_impl(int x, int fa) { sz[x] = 1; f[x] = 0; adj(i, x) if (!vis[v[i]] && v[i] != fa && !no[i]) { getroot_impl(v[i], x); sz[x] += sz[v[i]]; f[x] = max(f[x], sz[v[i]]); } f[x] = max(f[x], sum - sz[x]); if (f[x] < f[rt]) rt = x; } int getroot(int x, int fa, int sz) { sum = sz; f[rt = 0] = inf; getroot_impl(x, fa); return rt; } void dfs_depth(int x, int fa, int d) { sk[++top] = d; adj(i, x) if (v[i] != fa && !vis[v[i]] && !no[i]) dfs_depth(v[i], x, d + 1); } void divide(int x) { using namespace SBT; vis[x] = 1; reset(); adj(i, x) if (!vis[v[i]] && !no[i]) { dfs_depth(v[i], x, 1); FOR(j,1,top) ans += greaterthan(root, K - sk[j] - 1); while (top) insert(root, sk[top--]); } ans += greaterthan(root, K - 1); adj(i, x) if (!vis[v[i]] && !no[i] && sz[v[i]] >= K) divide(getroot(v[i], x, sz[v[i]])); } void dfs_circle(int x, int fa) { if (cir) return; if (vis[x]) { c[cir = 1] = x; int j = top; while (sk[j] != x) c[++cir] = sk[j--]; return; } vis[x] = 1; sk[++top] = x; adj(i, x) if (v[i] != fa) dfs_circle(v[i], x); vis[x] = 0; --top; } void work() { using namespace SBT; dfs_circle(1, 0); adj(i, c[1]) if (v[i] == c[cir]) { no[i] = no[i ^ 1] = 1; break; } divide(getroot(1, 0, n)); FOR(i,1,n) vis[i] = 0; reset(); FOR(i,1,cir) vis[c[i]] = 1; FOR(i,1,cir) { vis[c[i]] = 0; dfs_depth(c[i], 0, 0); vis[c[i]] = 1; FOR(j,1,top) ans += greaterthan(root, K - (cir - i + 1) - sk[j]); while (top) insert(root, sk[top--] + i, 1); } } int main() { int m, a, b; scanf("%d%d%d", &n, &m, &K); FOR(i,1,m) { scanf("%d%d", &a, &b); edge(a, b), edge(b, a); } if (n == m) work(); else divide(getroot(1, 0, n)); printf("%lld\n", ans); return 0; }
3648: 寝室管理
Description
T64有一个好朋友,叫T128。T128是寄宿生,并且最近被老师叫过去当宿管了。宿管可不是一件很好做的工作,碰巧T128有一个工作上的问题想请T64帮忙解决。T128的寝室条件不是很好,所以没有很多钱来装修。礼间寝室仅由n-1条双向道路连接,而且任意两间寝室之间都可以互达。最近,T128被要求对一条路径上的所有寝室进行管理,这条路径不会重复经过某个点或某条边。但他不记得是哪条路径了。他只记得这条路径上有不少于k个寝室。于是,他想请T64帮忙数一下,有多少条这样的路径满足条件。
嗯…还有一个问题。由于最近有一些熊孩子不准晚上讲话很不爽,他们决定修筑一条“情报通道”,如果通道建成,寝室就变成了一个N个点N条边的无向图。并且,经过“情报通道”的路径也是合法的。T128心想:通道建成之前,T64还有一个高效的算法帮我数路径条数,但是通道建成之后,他还有办法吗?对,T64手忙脚乱,根本数不清有多少条路径。于是他找到了你。
Input
第一行为三个正整数N,M,K(2 ≤ K ≤ N),代表有n间寝室,m条边连接它们n-1 ≤ m ≤ N;m= n-1意味着“情报遁道”未被修好;m=n意味着“情报通道”已被修好),以及题目描述中的K。接下来m行,每行两个正整数z,y,代表第x间寝室与第y间寝室之间有一条双向边。
Output
仅包含一个整数,代表经过至少K间寝室的路径条数。Sample Input
5 5 2 1 3 2 4 3 5 4 1 5 2
Sample Output
20
HINT
N≤100000,K≤N,M=Norz 黄学长的Program 唉?!404了?!
#include <cstdio> #include <cstring> #include <algorithm> #define FOR(i,j,k) for(i=j;i<=k;++i) typedef long long ll; using namespace std; const int N = 100005, M = N * 2; ll ans; int h , p[M], v[M], cnt, n, K; int f , sz , dep , sum, rt; int stk , top, c[N * 3], tot, cir[M], clen; bool instk , vis ; void add(int a, int b) { v[++cnt] = b; p[cnt] = h[a]; h[a] = cnt; } int query(int i) { if (i < 1) return 0; int s = 0; for (; i; i -= i&-i) s += c[i]; return s; } void modify(int i, int x) { tot += x; for (; i <= 2 * clen + n; i += i & -i) c[i] += x; } void root(int x, int fa) { f[x] = 0; sz[x] = 1; for (int i = h[x]; i; i = p[i]) if (v[i] != fa && !vis[v[i]]) { root(v[i], x); sz[x] += sz[v[i]]; f[x] = max(f[x], sz[v[i]]); } f[x] = max(f[x], sum - sz[x]); if (f[x] < f[rt]) rt = x; } int get_root(int x, int fa, int sz) { rt = 0; sum = sz; f[0] = sz + 1; root(x, fa); return rt; } void depth(int x, int fa) { stk[++top] = dep[x]; for (int i = h[x]; i; i = p[i]) if (v[i] != fa && !vis[v[i]]) { dep[v[i]] = dep[x] + 1; depth(v[i], x); } } void solve(int x) { int i, j; vis[x] = 1; for (i = h[x]; i; i = p[i]) if (!vis[v[i]]) { dep[v[i]] = 1; depth(v[i], x); FOR(j,1,top) { if (stk[j] + 1 >= K) ++ans; ans += tot - query(K - stk[j] - 2); } while (top) modify(stk[top--], 1); } for (i = h[x]; i; i = p[i]) if (!vis[v[i]]) { depth(v[i], x); while (top) modify(stk[top--], -1); } for (i = h[x]; i; i = p[i]) if (!vis[v[i]]) { if (sz[v[i]] < K) continue; solve(get_root(v[i], x, sz[v[i]])); } } void circle(int x,int fa) { if (clen) return; stk[++top] = x; instk[x] = 1; for (int i = h[x]; i; i = p[i]) if (v[i] != fa) { if (instk[v[i]]) { while (stk[top] != v[i]) cir[++clen] = stk[top--]; cir[++clen] = v[i]; } else circle(v[i], x); } top--; instk[x] = 0; } void solvecir() { int i, j; circle(1, 0); top = 0; int dis = 2 * clen; FOR(i,1,clen) vis[cir[i + clen] = cir[i]] = 1; FOR(i,1,clen) { vis[cir[i]] = 0; solve(get_root(cir[i], 0, n)) vis[cir[i]] = 1; } FOR(i,1,n) vis[i] = 0; FOR(i,1,clen) vis[cir[i]] = 1; FOR(i,1,2 * clen) { if (i > clen) { vis[cir[i - clen]] = 0; depth(cir[i - clen], 0); vis[cir[i - clen]] = 1; FOR(j,1,top) modify(stk[j] + dis + clen, -1); FOR(j,1,top) ans += tot - query(K + dis - stk[j]); top = 0; } dep[cir[i]] = 1; vis[cir[i]] = 0; depth(cir[i], 0); vis[cir[i]] = 1; while (top) modify(stk[top--] + dis, 1); --dis; } } int main() { int i, a, b, m; scanf("%d%d%d", &n, &m, &K); FOR(i,1,m) { scanf("%d%d", &a, &b); add(a, b); add(b, a); } if (n == m) solvecir(); else solve(get_root(1, 0, n)) printf("%lld\n", ans); return 0; }
相关文章推荐
- 全国行政区划代码具体到县
- 接口回调
- linux之crond服务
- memcache的使用http://blog.csdn.net/scelong/article/details/7245343
- Android仿斗鱼滑动登录验证
- nyoj_1 A+B Problem
- Swift语言精要 - 浅谈结构体(Struct)
- Netty in Action (二十五) 自我总结
- java.lang.String的intern()方法
- 类的重写
- 《Motion Design for iOS》(六)
- Swift语言精要 - 浅谈结构体(Struct)
- java 代码
- 在kendogrid中控制数据格式以及实现求和
- tomcat警告解决办法:WARNING: An attempt was made to authenticate the locked user
- 【数据库】MySQL性能优化之swap占用高
- 区块链网络动力学对世界的解构和重构
- 集合框架(ArrayList存储字符串并遍历)
- matlab plot三维图形
- idea切换SVN地址图解