洛谷P5163 WD与地图
只有洛谷的毒瘤才会在毒瘤月赛里出毒瘤题......
题意:三个操作,删边,改变点权,求点x所在强连通分量内前k大点权之和。
解:狗屎毒瘤数据结构乱堆......
整体二分套(tarjan+并查集) + 线段树合并。
首先可以变成加边。
然后就是神奇操作让人难以置信......
对于每条边,我们有个时刻t使得它的两端点在同一scc内。那么如何求出这个t呢?
整体二分!...这又是怎么想到的啊......
对于一个时刻mid,我们把小于它的边提取出来缩点,如果有的边两端点在一个scc,那么它的t就不大于mid。否则大于mid。
为了保证整体二分的复杂度,我们每次操作tarjan的图不能太大。具体来说,把之前做过的区间的scc用并查集缩起来。
对于每条边,提取两端点所在scc的代表元为关键点,构出虚图(?????),然后tarjan。
之后递归左边,之后并查集缩点,之后递归右边。
所有t求出之后,再倒着跑一遍询问,按照时刻把边的两端点在并查集中合并(表示成为一个scc),并对权值线段树进行合并。
查询就是所在scc的权值线段树的前k大之和,这个好办。
注意爆int。我写了300行7k......
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#include <stack>
#include <map> typedef long long LL;
const int N = , M = , V = ; struct Edge {
int nex, v;
}edge[M]; int tp; struct Node {
int t, x, y, id;
}node[M], t1[M], t2[M]; struct Ask {
int f, x, y;
LL ans;
}ask[M]; std::vector<Node> v[M];
std::stack<int> S;
std::map<int, int> mp[N];
int e[N], fr[N], stk[N], top, dfn[N], low[N], use[N], X[N + M], xx, rt[N], sum[V], ls[V], rs[V], val[N];
int Time, scc_cnt, num, n, m, q, tot;
LL Val[V]; inline void add(int x, int y) {
tp++;
edge[tp].v = y;
edge[tp].nex = e[x];
e[x] = tp;
return;
} namespace ufs {
int fa[N];
int find(int x) {
if(fa[x] == x) {
return x;
}
return fa[x] = find(fa[x]);
}
inline void merge(int x, int y) {
fa[find(x)] = find(y);
return;
}
inline void clear() {
for(int i = ; i <= n; i++) {
fa[i] = i;
}
return;
}
} void tarjan(int x) {
low[x] = dfn[x] = ++num;
S.push(x);
//printf("tarjan %d \n", x);
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(!dfn[y]) {
tarjan(y);
low[x] = std::min(low[x], low[y]);
}
else if(!fr[y]) { // find
low[x] = std::min(low[x], dfn[y]);
}
}
if(low[x] == dfn[x]) {
//printf("getscc \n");
++scc_cnt;
int y;
do {
y = S.top();
//printf("%d ", y);
S.pop();
fr[y] = scc_cnt;
//ufs::merge(x, y);
} while(y != x);
//puts("");
}
return;
} inline void TAR() {
scc_cnt = num = ;
for(int i = ; i <= top; i++) {
if(!dfn[stk[i]]) {
tarjan(stk[i]);
}
}
return;
} inline void link(int x, int y) {
x = ufs::find(x); y = ufs::find(y);
if(use[x] != Time) {
use[x] = Time;
dfn[x] = fr[x] = e[x] = ;
stk[++top] = x;
}
if(use[y] != Time) {
use[y] = Time;
dfn[y] = fr[y] = e[y] = ;
stk[++top] = y;
}
add(x, y); // self-circle is OK
return;
} inline void solve(int L, int R, int l, int r) {
if(R < L) {
return;
}
//printf("solve %d %d %d %d \n", L, R, l, r);
if(l == r) {
for(int i = L; i <= R; i++) {
v[r].push_back(node[i]);
}
return;
}
int mid = (l + r) >> ;
++Time; top = tp = ;
for(int i = L; i <= R; i++) {
if(node[i].t <= mid) {
link(node[i].x, node[i].y);
}
}
//int ufs_t = ufs::top;
TAR();
int top1 = , top2 = ;
for(int i = L; i <= R; i++) {
int x = ufs::find(node[i].x), y = ufs::find(node[i].y);
if(node[i].t <= mid && fr[x] == fr[y]) {
t1[++top1] = node[i];
}
else {
t2[++top2] = node[i];
}
}
memcpy(node + L, t1 + , top1 * sizeof(Node));
memcpy(node + L + top1, t2 + , top2 * sizeof(Node));
solve(L, L + top1 - , l, mid);
for(int i = L; i < L + top1; i++) {
int x = ufs::find(node[i].x);
int y = ufs::find(node[i].y);
ufs::merge(x, y);
}
solve(L + top1, R, mid + , r);
return;
} int merge(int x, int y) {
if(!x || !y || x == y) {
return x | y;
}
int o = ++tot;
sum[o] = sum[x] + sum[y];
Val[o] = Val[x] + Val[y];
ls[o] = merge(ls[x], ls[y]);
rs[o] = merge(rs[x], rs[y]);
return o;
} void add(int p, int v, int l, int r, int &o) {
//printf("add : %d %d %d %d \n", p, v, l, r);
if(!o) {
o = ++tot;
}
if(l == r) {
sum[o] += v;
Val[o] += v * X[r];
return;
}
int mid = (l + r) >> ;
if(p <= mid) {
add(p, v, l, mid, ls[o]);
}
else {
add(p, v, mid + , r, rs[o]);
}
sum[o] = sum[ls[o]] + sum[rs[o]];
Val[o] = Val[ls[o]] + Val[rs[o]];
return;
} LL query(int k, int l, int r, int o) {
//printf("query %d [%d %d] \n", k, l, r);
if(!o) {
return ;
}
if(l == r) {
return 1ll * std::min(k, sum[o]) * X[r];
}
int mid = (l + r) >> ;
if(sum[rs[o]] >= k) {
return query(k, mid + , r, rs[o]);
}
else {
return Val[rs[o]] + query(k - sum[rs[o]], l, mid, ls[o]);
}
} int main() { scanf("%d%d%d", &n, &m, &q);
ufs::clear();
for(int i = ; i <= n; i++) {
scanf("%d", &val[i]);
X[++xx] = val[i];
}
for(int i = ; i <= m; i++) {
scanf("%d%d", &node[i].x, &node[i].y);
mp[node[i].x][node[i].y] = i;
node[i].id = i;
}
for(int i = ; i <= q; i++) {
scanf("%d%d%d", &ask[i].f, &ask[i].x, &ask[i].y);
if(ask[i].f == ) {
ask[i].y += val[ask[i].x];
X[++xx] = ask[i].y;
std::swap(val[ask[i].x], ask[i].y);
}
}
std::sort(X + , X + xx + );
xx = std::unique(X + , X + xx + ) - X - ;
for(int i = ; i <= n; i++) {
val[i] = std::lower_bound(X + , X + xx + , val[i]) - X;
}
for(int i = ; i <= q; i++) {
if(ask[i].f == ) {
ask[i].y = std::lower_bound(X + , X + xx + , ask[i].y) - X;
}
}
std::reverse(ask + , ask + q + );
for(int i = ; i <= q; i++) {
if(ask[i].f == ) {
int t = mp[ask[i].x][ask[i].y];
node[t].t = i;
}
} solve(, m, , q + );
//printf("--------------------------------\n");
ufs::clear();
for(int i = ; i <= n; i++) {
add(val[i], , , xx, rt[i]);
}
for(int i = ; i <= q; i++) {
//printf("i = %d \n", i);
for(int j = ; j < v[i].size(); j++) {
//printf(" > edge = %d %d \n", v[i][j].x, v[i][j].y);
int x = ufs::find(v[i][j].x), y = ufs::find(v[i][j].y);
ufs::merge(x, y);
int t = ufs::find(x);
rt[t] = merge(rt[x], rt[y]);
//printf("%d = merge %d %d \n", t, x, y);
}
if(ask[i].f == ) {
int t = ufs::find(ask[i].x);
ask[i].ans = query(ask[i].y, , xx, rt[t]);
}
else if(ask[i].f == ) {
int t = ufs::find(ask[i].x);
//printf("change %d : %d -> %d \n", ask[i].x, X[val[ask[i].x]], X[ask[i].y]);
add(val[ask[i].x], -, , xx, rt[t]);
add(ask[i].y, , , xx, rt[t]);
val[ask[i].x] = ask[i].y;
}
}
for(int i = q; i >= ; i--) {
if(ask[i].f == ) {
printf("%lld\n", ask[i].ans);
}
}
return ;
}
AC代码
对拍真是个好东西。
洛谷P5163 WD与地图的更多相关文章
- 题解 洛谷 P5163 【WD与地图】
首先将操作倒序,把删边转化为加边.先考虑若边是无向边,条件为连通,要怎么处理. 可以用并查集来维护连通性,对每个连通块维护一颗权值线段树,连通块的合并用线段树合并来实现,线段树同时也支持了修改点权. ...
- 洛谷 P5162 WD与积木 解题报告
P5162 WD与积木 题目背景 WD整日沉浸在积木中,无法自拔-- 题目描述 WD想买\(n\)块积木,商场中每块积木的高度都是\(1\),俯视图为正方形(边长不一定相同).由于一些特殊原因,商家会 ...
- 洛谷P5159 WD与矩阵
题目背景 WD整日沉浸在矩阵中,无法自拔-- 题目描述 WD特别喜欢矩阵,尤其是\(01\)矩阵. 一天,CX给了WD一个巨大的\(n\)行\(m\)列的\(01\)矩阵,WD发现这个矩阵每行.每列的 ...
- 洛谷P5162 WD与积木 [DP,NTT]
传送门 思路 真是非常套路的一道题-- 考虑\(DP\):设\(f_n\)为\(n\)个积木能搭出的方案数,\(g_n\)为所有方案的高度之和. 容易得到转移方程: \[ \begin{align*} ...
- 洛谷 P5162 WD与积木【多项式求逆】
设f[i]为i个积木能堆出来的种类,g[i]为i个积木能堆出来的种类和 \[ f[n]=\sum_{i=1}^{n}C_{n}^{i}g[n-i] \] \[ g[n]=\sum_{i=1}^{n}C ...
- P5163 WD与地图(整体二分+权值线段树)
传送门 细节要人命.jpg 这题思路太新奇了--首先不难发现可以倒着做变成加边,但是它还需要我们资瓷加边的同时维护强连通分量.显然加边之后暴力跑是不行的 然后有一个想法,对于一条边\((u,v)\), ...
- 洛谷P5160 WD与循环
我们看这段代码 int cnt = 0; for (int a_1 = 0; a_1 <= m; a_1++) { for (int a_2 = 0; a_1 + a_2 <= m; a_ ...
- P5163 WD与地图 [整体二分,强连通分量,线段树合并]
首先不用说,倒着操作.整体二分来做强连通分量,然后线段树合并,这题就做完了. // powered by c++11 // by Isaunoya #include <bits/stdc++.h ...
- 关于三目运算符与if语句的效率与洛谷P2704题解
题目描述 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下图.在每一格平原地形上最 ...
随机推荐
- su: 无法设置用户ID: 资源暂时不可用
登录非root用户,报错如下:[root@test ~]# su - appsu: 无法设置用户ID: 资源暂时不可用 或者用ssh 命令连接服务器之后,如果一段时间不操作,再次进入 Terminal ...
- mac系统下修复第三方Python包bug
发现问题 今天在github上fork了CI 3.x的中文手册,按照README文档一步步进行Sphinx和相关工具的安装,最终build生成html版手册.操作到第6步执行`make html`的时 ...
- Linux内核及分析 第五周 扒开系统调用的三层皮(下)
实验内容: 1.执行rm menu -rf命令,强制删除原有的menu 2.使用git命令 git clone https://github.com/mengning/menu.git 克隆新的men ...
- Python小笔记
最近在学习Python 以前也学过一些编程语言 但是都不扎实 由于工作的原因 需要学习Python做一些处理 刚开始学习总有多多少少的不适应 从今天开始打算将我在学习中遇到的困难以及疑问记录下来 ...
- [福大软工] Z班——Alpha现场答辩情况汇总
Alpha现场答辩 小组互评(文字版) 各组对于 麻瓜制造者 的评价与建议 队伍名 评价与建议 *** 界面较友好,安全性不足,功能基本完整.希望能留下卖家的联系方式而不是在APP上直接联系,APP上 ...
- CMake系列之四:多个源文件-多个目录
多个源文件,多个目录 现在进一步将MathFunctions.c和MathFunctions.h文件移到math目录下: ./Demo3 | +--- main.c | +--- math/ | +- ...
- PAT (Basic Level) Practice 1001 害死人不偿命的(3n+1)猜想
https://pintia.cn/problem-sets/994805260223102976/problems/994805325918486528 卡拉兹(Callatz)猜想: 对任何一个自 ...
- DockerHub使用简介
常用的Docker镜像文件都有,就不用自己费劲的一点点配置了,这才是Docker的真正目的.就像Ghost里边含office,直接还原,不用一台台机器安装呢,省时省力,与高效工作的理念相契合. 至于, ...
- FuelPHP 系列(六) ------ CURD 增删改查
一.create $article = new Model_Article(); // 或 $article = Model_Article::forge(); // 保存数据,返回新增数据 id $ ...
- php开发APP接口(总结一)
一.什么是app接口:服务端与客户端的数据交互. 大部分APP接口是通过http协议通信的. http通信的三要素: URL: 通信的地址 Method:通信的方式(get | post | pu ...