hdu 5467 Clarke and hunger games (lct)
来源:互联网 发布:比特币挖矿需要网络吗 编辑:程序博客网 时间:2024/05/30 23:10
Clarke and hunger games
Accepts: 2
Submissions: 54
Time Limit: 6000/3000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
问题描述
克拉克是一名人格分裂患者。某一天,克拉克fork出了很多个克拉克,居住在n个社区里。 克拉克由于自己的抖M属性,所以克拉克们决定玩饥饿游戏。 由于克拉克们住在天空之城中,所以他们相互联系靠的是社区之间的桥。 克拉克是非常节约的,两个社区能相互可达时只有一条路径,这样就节约了不少金钱。 饥饿游戏的规则是这样的:每一个社区选出两个人,然后作为供品去参加死亡比赛,最终活下一个人,并终身获得自由,获得无限的荣誉和金钱。 克拉克们想知道选出的人中有多少种组合。两个组合相同当且仅当选出的人都相同。 由于克拉克的抖M属性,克拉克还有q次操作: 1 u v:在u,v两个社区之间建桥。如果u,v已经有路径或u=v相连则忽略。 2 u v:删除u,v两个社区的之间的桥。如果u,v之间没有桥或u=v则忽略。 3 h:变回到第h次操作后的状态,当h=0时表示回到初始状态,即社区之间是没有桥的。 4 u v:询问u到v路径上的所有社区(包括u和v)每个社区选出人参加比赛的方案数(如果不存在路径输出0),由于方案数太多,所以求出对109+7取模的答案。 5 u s:社区u中的克拉克的数量变为s。一开始克拉克的社区之间是没有桥的。
输入描述
第一行一个整数T(1≤T≤5),表示数据的组数。 每组数据第一行有两个整数n,q(1≤n,q≤3∗105)。 第二行有n个整数,第i个数ai(0≤ai≤104)表示第i个社区的人数。接下来q行,第i个询问的第一个数为opt。 当opt=1时,后面接着两个整数ui,vi。 当opt=2时,后面接着两个整数ui,vi。 当opt=3时,后面接着一个整数hi。 当opt=4时,后面接着两个整数ui,vi。 当opt=5时,后面接着两个整数ui,si。1≤ui,vi≤n,0≤hi<i,0≤si≤104
输出描述
对于每组数据的opt=4,输出一个整数表示答案。
输入样例
13 51 2 31 1 22 1 23 15 1 34 1 2
输出样例
3
lct,对于回退操作,直接暴力处理,建立一棵操作树,每个操作最多正着,反着执行一次,时间复杂度为2nlog(n)
#include <iostream>#include <cstdio>#include <set>#include <vector>#include <cstring>#include <algorithm>using namespace std;#define LL __int64#define LS(x) node[(x)].ch[0]#define RS(x) node[(x)].ch[1]#define P pair<int ,int>#define X first#define Y second#define PB push_backconst int N = 3 * 1e5 + 5;const int MOD = 1000000007;int a[N];set<P> used;set<P>::iterator it;struct LCT { struct Node { int fa, ch[2]; bool rev; int size; void init(LL x) { fa = 0; rev = ch[0] = ch[1] = 0; size = (LL)x * (x - 1) / 2; } } node[N]; bool is_root(int x) { return (LS(node[x].fa) != x && RS(node[x].fa) != x); } void down(int x) { if(node[x].rev) { node[LS(x)].rev ^= 1; node[RS(x)].rev ^= 1; swap(LS(x), RS(x)); node[x].rev = 0; } } void up(int x) { node[x].size = (LL)a[x] * (a[x] - 1) / 2 * node[LS(x)].size % MOD * node[RS(x)].size % MOD; } void rotate(int x, bool kind) { int fx = node[x].fa; int ffx = node[fx].fa; node[fx].ch[!kind] = node[x].ch[kind]; node[node[x].ch[kind]].fa = fx; if(!is_root(fx)) node[ffx].ch[RS(ffx) == fx] = x; node[x].fa = ffx; node[x].ch[kind] = fx; node[fx].fa = x; up(fx); } int d_st[N], ds_top; void splay(int x) { ds_top = -1; d_st[++ds_top] = x; for(int i = x; !is_root(i); i = node[i].fa) d_st[++ds_top] = node[i].fa; for(int i = ds_top; i >= 0 ; --i) down(d_st[i]); while(!is_root(x)) { int fx = node[x].fa; int ffx = node[fx].fa; bool rot_x = (LS(fx) == x); bool rot_fx = (LS(ffx) == fx); if(is_root(fx)) rotate(x, rot_x); else { if(rot_x == rot_fx) rotate(fx, rot_fx); else rotate(x, rot_x); rotate(x, rot_fx); } } up(x); } void access(int x) { int last = 0; while(x) { splay(x); RS(x) = last; last = x; x = node[x].fa; } } //access(x); splay(x); 后x在根的位置,此时x只有左子树,没有右子树,打翻转标记就是使得左子树变到右子树上。 void make_root(int x) { access(x); splay(x); node[x].rev ^= 1; } int find(int x) { access(x); splay(x); while(LS(x)) x = LS(x); return x; } bool cut(int x, int y) { if(x > y) swap(x, y); it = used.find(P(x, y)); if(it == used.end()) return false; make_root(x); access(y); splay(y); LS(y) = node[x].fa = 0; up(y); used.erase(it); return true; } bool link(int x, int y) { if(x > y) swap(x, y); int fx = find(x); int fy = find(y); if(fx == fy) return false; make_root(x); node[x].fa = y; used.insert(P(x, y)); return true; } void init(int n) { for(int i = 0; i <= n; ++i) node[i].init(a[i]); node[0].size = 1; } void debug(int n) { for(int i = 0; i <= n; ++i) { cout<<i<<": "<<node[i].size<<' '<<node[i].fa<<' '<<node[i].ch[0]<<' '<<node[i].ch[1]<<endl; } } int get_ans(int x, int y) { int fx = find(x); int fy = find(y); if(fx != fy) return 0; make_root(x); access(y); splay(y); return node[y].size; } void dfs(int);} lct;vector<int> G[N];int opt[N], u[N], v[N];vector<P> ans;void LCT::dfs(int x) { for(int i = 0; i < G[x].size(); ++i) { int y = G[x][i]; if(opt[y] == 1) { bool flag = link(u[y], v[y]); dfs(y); if(flag) cut(u[y], v[y]); } else if(opt[y] == 2) { bool flag = cut(u[y], v[y]); dfs(y); if(flag) link(u[y], v[y]); } else if(opt[y] == 4) { //ans[y] = get_ans(u[y], v[y]); ans.PB(P(y, get_ans(u[y], v[y]))); dfs(y); } else if(opt[y] == 5) { int tmp = a[u[y]]; a[u[y]] = v[y]; node[u[y]].size = (LL)v[y] * (v[y] - 1) / 2; access(u[y]); splay(u[y]); dfs(y); a[u[y]] = tmp; node[u[y]].size = (LL)tmp * (tmp - 1) / 2; access(u[y]); splay(u[y]); } }}void gao(int n, int q) { lct.dfs(0); sort(ans.begin(), ans.end()); for(int i = 0; i < ans.size(); ++i) { printf("%d\n", ans[i].Y); }}void init(int n, int q) { lct.init(n); //lct.debug(n); for(int i = 0; i <= q; ++i) { G[i].clear(); } ans.clear(); used.clear();}int main() { int T; scanf("%d", &T); while(T--) { int n, q; scanf("%d%d", &n, &q); a[0] = 0; for(int i = 1; i <= n; ++i) { scanf("%d", &a[i]); } init(n, q); int pre = 0; for(int i = 1; i <= q; ++i) { scanf("%d", &opt[i]); if(opt[i] != 3) { scanf("%d%d", &u[i], &v[i]); G[pre].PB(i); pre = i; } else { scanf("%d", &u[i]); pre = u[i];while(opt[pre] == 3) pre = u[pre]; } } gao(n, q); } return 0;}
0 0
- hdu 5467 Clarke and hunger games (lct)
- [动态树] HDOJ 5467 Clarke and hunger games
- HDU Clarke and points
- Clarke and MST HDU
- HDU 5463 Clarke and minecraft
- HDU 5465 Clarke and puzzle
- HDU 5463 Clarke and minecraft
- HDU 5463 Clarke and minecraft
- HDU 5464:Clarke and problem
- HDU 5463Clarke and minecraft
- HDU 5465 Clarke and puzzle
- HDU 5464 Clarke and problem
- HDU-5463Clarke and minecraft
- hdu 5463 Clarke and minecraft
- HDU 5464Clarke and problem
- HDU 5565:Clarke and baton
- HDU 5565 Clarke and baton
- hdu 5565 Clarke and baton
- MFC制作加法计算器
- C#字符串比较
- ACM HDU 2063 过山车(简单的二分匹配)
- Java国际化语言切换
- DirectX11 纹理采样
- hdu 5467 Clarke and hunger games (lct)
- Android中Activity四种启动模式(LaunchMode)和taskAffinity属性
- Java字符串格式化
- 学习ThinkPHP3.2.2:video9,配置文件存放位置的变化
- PHP excel读取excel文件转换为数组
- 斐波纳契数列
- 学习ThinkPHP3.2.2:video9,用“C”函数读取配置文件内容
- 【bzoj3316】 JC loves Mkk 单调队列+二分答案
- spring mvc 异常统一处理