题解 洛谷 P5163 【WD与地图】
首先将操作倒序,把删边转化为加边。先考虑若边是无向边,条件为连通,要怎么处理。
可以用并查集来维护连通性,对每个连通块维护一颗权值线段树,连通块的合并用线段树合并来实现,线段树同时也支持了修改点权。
然后再考虑对于有向边和强连通分量要怎么做。无向边的作用是使两个连通块成为一个连通块,有向边的作用是使两个强连通分量成为一个强连通分量。但是加入一条有向边后,其两端的强连通分量不一定合并为一个强连通分量,随着其他边的加入,该边才有可能发挥其作用。
因此可以考虑对每条边二分出一个时刻,为加入该边后,随着边的加入,其两端强连通分量合并的最早时刻。单独考虑每条边不现实,采取整体二分来处理。对于当前二分的时间区间\([l,r]\),对于时间在\([0,mid]\)的所有边都加入,然后用\(tarjan\)来缩点,若满足条件就寻找更早的答案,不满足就尝试晚一些的答案。
发现在二分过程中每次都加入\([0,mid]\)的所有边来进行缩点,复杂度无法接受。对于当前的情况,可以利用之前二分得到的缩点结果,通过并查集来维护每个点所在的强连通分量,连边直接在强连通分量之间连边即可。对于二分的区间\([l,r]\),已经连好了区间\([0,mid]\)的边,递归时先进入区间\([mid+1,r]\),这样就能利用上之前的连边情况,递归进入区间\([l,mid]\)之前,需要清除之前连边的影响,这里用可撤销并查集实现即可。
二分出时刻后,类比无向图的处理就行了。
\(code:\)
#include<bits/stdc++.h>
#define maxn 800010
#define maxm 10000010
#define mid ((l+r)>>1)
#define mk make_pair
#define ask(x) (lower_bound(s+1,s+tot+1,x)-s)
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,m,qu,top,tot,cnt,dfn_cnt,s_top,tree_cnt;
int size[maxn],fa[maxn],t[maxn],dfn[maxn],low[maxn],sta[maxn];
int ls[maxm],rs[maxm],rt[maxn],siz[maxm];
ll a[maxn],s[maxn],sum[maxm],ans[maxn];
bool vis[maxn];
map<pair<int,int>,int> mp;
struct edge
{
    int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
    e[++edge_cnt]=(edge){to,head[from]};
    head[from]=edge_cnt;
}
struct node
{
    int x,y,t,opt;
}p[maxn],p1[maxn],p2[maxn],q[maxn];
bool cmp(const node &a,const node &b)
{
    if(a.t==b.t) return a.opt<b.opt;
    return a.t<b.t;
}
struct Stack
{
    int x,y;
}st[maxn];
int find(int x)
{
    return fa[x]==x?x:find(fa[x]);
}
void merge(int x,int y)
{
    x=find(x),y=find(y);
    if(x==y) return;
    if(size[x]<size[y]) swap(x,y);
    st[++top]=(Stack){x,y},size[x]+=size[y],fa[y]=x;
}
void del(int id)
{
    int x=st[id].x,y=st[id].y;
    fa[y]=y,size[x]-=size[y];
}
void tarjan(int x)
{
    dfn[x]=low[x]=++dfn_cnt,sta[++s_top]=x,vis[x]=true;
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
        else if(vis[y]) low[x]=min(low[x],dfn[y]);
    }
    if(dfn[x]==low[x])
    {
        int p=sta[s_top--],now;
        vis[p]=false;
        if(p==x) return;
        do
        {
            now=sta[s_top--],vis[now]=false,merge(p,now);
        }while(now!=x);
    }
}
void clear(int n)
{
    edge_cnt=dfn_cnt=0;
    for(int i=1;i<=n;++i) head[t[i]]=dfn[t[i]]=low[t[i]]=0;
}
void solve(int L,int R,int l,int r)
{
    if(L>R) return;
    if(l==r)
    {
        for(int i=L;i<=R;++i) p[i].t=l;
        return;
    }
    int now=top,num=0,cnt1=0,cnt2=0;
    for(int i=L;i<=R;++i)
    {
        if(p[i].t>mid) continue;
        int x=find(p[i].x),y=find(p[i].y);
        t[++num]=x,t[++num]=y,add(x,y);
    }
    for(int i=1;i<=num;++i)
        if(!dfn[t[i]])
            tarjan(t[i]);
    for(int i=L;i<=R;++i)
    {
        if(p[i].t<=mid&&find(p[i].x)==find(p[i].y)) p1[++cnt1]=p[i];
        else p2[++cnt2]=p[i];
    }
    for(int i=1;i<=cnt1;++i) p[L+i-1]=p1[i];
    for(int i=1;i<=cnt2;++i) p[L+cnt1+i-1]=p2[i];
    clear(num),solve(L+cnt1,R,mid+1,r);
    while(top>now) del(top--);
    solve(L,L+cnt1-1,l,mid);
}
void modify(int l,int r,int pos,int v,int &cur)
{
    if(!cur) cur=++tree_cnt;
    siz[cur]+=v,sum[cur]+=s[pos]*v;
    if(l==r) return;
    if(pos<=mid) modify(l,mid,pos,v,ls[cur]);
    else modify(mid+1,r,pos,v,rs[cur]);
}
ll query(int l,int r,int k,int cur)
{
    if(k>siz[cur]) return sum[cur];
    if(l==r) return s[l]*k;
    if(k<=siz[rs[cur]]) return query(mid+1,r,k,rs[cur]);
    return query(l,mid,k-siz[rs[cur]],ls[cur])+sum[rs[cur]];
}
int merge(int x,int y,int l,int r)
{
    if(!x||!y) return x+y;
    if(l==r)
    {
        siz[x]+=siz[y],sum[x]+=sum[y];
        return x;
    }
    ls[x]=merge(ls[x],ls[y],l,mid),rs[x]=merge(rs[x],rs[y],mid+1,r);
    siz[x]=siz[ls[x]]+siz[rs[x]],sum[x]=sum[ls[x]]+sum[rs[x]];
    return x;
}
int main()
{
    read(n),read(m),read(qu),cnt=m;
    for(int i=1;i<=n;++i)
        read(a[i]),s[++tot]=a[i],fa[i]=i,size[i]=1;
    for(int i=1;i<=m;++i)
        read(p[i].x),read(p[i].y),mp[mk(p[i].x,p[i].y)]=i;
    for(int i=1;i<=qu;++i)
    {
        read(q[i].opt),read(q[i].x),read(q[i].y),q[i].t=qu-i+1;
        if(q[i].opt==1) p[mp[mk(q[i].x,q[i].y)]].t=q[i].t;
        if(q[i].opt==2) a[q[i].x]+=q[i].y,s[++tot]=a[q[i].x];
    }
    sort(s+1,s+tot+1),tot=unique(s+1,s+tot+1)-s-1,solve(1,m,0,qu+1);
    for(int i=1;i<=n;++i)
        modify(1,tot,ask(a[i]),1,rt[i]),fa[i]=i,size[i]=1;
    for(int i=1;i<=qu;++i)
        if(q[i].opt!=1)
            p[++cnt]=q[i];
    sort(p+1,p+cnt+1,cmp);
    for(int i=1;i<=cnt;++i)
    {
        int x=p[i].x,y=p[i].y,t=p[i].t,opt=p[i].opt,f;
        if(!opt)
        {
            x=find(x),y=find(y);
            if(x==y) continue;
            if(size[x]<size[y]) swap(x,y);
            size[x]+=size[y],fa[y]=x;
            rt[x]=merge(rt[x],rt[y],1,tot);
        }
        if(opt==2)
        {
            f=find(x),modify(1,tot,ask(a[x]),-1,rt[f]);
            a[x]-=y,modify(1,tot,ask(a[x]),1,rt[f]);
        }
        if(opt==3) f=find(x),ans[qu-t+1]=query(1,tot,y,rt[f]);
    }
    for(int i=1;i<=qu;++i)
        if(q[i].opt==3)
            printf("%lld\n",ans[i]);
    return 0;
}
												
											题解 洛谷 P5163 【WD与地图】的更多相关文章
- 洛谷P5163 WD与地图
		
只有洛谷的毒瘤才会在毒瘤月赛里出毒瘤题...... 题意:三个操作,删边,改变点权,求点x所在强连通分量内前k大点权之和. 解:狗屎毒瘤数据结构乱堆...... 整体二分套(tarjan+并查集) + ...
 - 题解 洛谷P5018【对称二叉树】(noip2018T4)
		
\(noip2018\) \(T4\)题解 其实呢,我是觉得这题比\(T3\)水到不知道哪里去了 毕竟我比较菜,不大会\(dp\) 好了开始讲正事 这题其实考察的其实就是选手对D(大)F(法)S(师) ...
 - 题解 洛谷P1562 【还是N皇后】
		
原题:洛谷P1562 这个题的原理和8皇后的原理是一模一样的,就是必须要用n个皇后把每一个行填满,同时满足每一列,每一行,每一条对角线只有一个棋子.但如果按照原来的方法暴打的话只有60分(优化亲测无效 ...
 - 题解 洛谷 P3396 【哈希冲突】(根号分治)
		
根号分治 前言 本题是一道讲解根号分治思想的论文题(然鹅我并没有找到论文),正 如论文中所说,根号算法--不仅是分块,根号分治利用的思想和分块像 似却又不同,某一篇洛谷日报中说过,分块算法实质上是一种 ...
 - 题解-洛谷P5410 【模板】扩展 KMP(Z 函数)
		
题面 洛谷P5410 [模板]扩展 KMP(Z 函数) 给定两个字符串 \(a,b\),要求出两个数组:\(b\) 的 \(z\) 函数数组 \(z\).\(b\) 与 \(a\) 的每一个后缀的 L ...
 - 题解-洛谷P4229 某位歌姬的故事
		
题面 洛谷P4229 某位歌姬的故事 \(T\) 组测试数据.有 \(n\) 个音节,每个音节 \(h_i\in[1,A]\),还有 \(m\) 个限制 \((l_i,r_i,g_i)\) 表示 \( ...
 - 题解-洛谷P4724 【模板】三维凸包
		
洛谷P4724 [模板]三维凸包 给出空间中 \(n\) 个点 \(p_i\),求凸包表面积. 数据范围:\(1\le n\le 2000\). 这篇题解因为是世界上最逊的人写的,所以也会有求凸包体积 ...
 - 题解-洛谷P4859 已经没有什么好害怕的了
		
洛谷P4859 已经没有什么好害怕的了 给定 \(n\) 和 \(k\),\(n\) 个糖果能量 \(a_i\) 和 \(n\) 个药片能量 \(b_i\),每个 \(a_i\) 和 \(b_i\) ...
 - 题解-洛谷P5217 贫穷
		
洛谷P5217 贫穷 给定长度为 \(n\) 的初始文本 \(s\),有 \(m\) 个如下操作: \(\texttt{I x c}\),在第 \(x\) 个字母后面插入一个 \(c\). \(\te ...
 
随机推荐
- .NET Core请求控制器Action方法正确匹配,但为何404?
			
前言 有些时候我们会发现方法名称都正确匹配,但就是找不到对应请求接口,所以本文我们来深入了解下何时会出现接口请求404的情况. 匹配控制器Action方法(404) 首先我们创建一个web api应用 ...
 - mysql8.0 解决时区问题
			
jdbc:mysql://localhost:3306/databaseName?useUnicode=true&characterEncoding=UTF-8&useOldAlias ...
 - 【SpringMVC】
			
前言
 - bug和测试报告思维导图
			
Bug定义: 1.不符合需求的 2.程序本身的报错 3.不符合用户使用习惯的 Bug生命周期: 当我们测试人员提交一个bug的时候,自始bug就有它的生命周期,从开始到结束, 把测试的过程和结果写成文 ...
 - 基数排序(Java)
			
基数排序(Java) 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 基数排序(桶排序)介绍 基数排序(radix sort)属 ...
 - Digix2019华为算法精英挑战赛代码
			
Digix2019华为算法精英挑战赛代码 最终成绩: 决赛第九 问题 根据手机型号,颜色,用户偏好,手机APP等信息预测用户年龄. https://developer.huawei.com/consu ...
 - CSS背景颜色透明
			
{ filter:alpha(opacity=50); -moz-opacity:0.5; -khtml-opacity: 0.5; opacity: 0.5; } 兼容大部分主流浏览器 filter ...
 - 使用selenium抓取淘宝信息并存储mongodb
			
selenium模块 简单小例子 Author:song import pyquery from selenium import webdriver from selenium.common.exce ...
 - C语言学习笔记第一章——开篇
			
本文章B站有对应视频 (本文图片.部分文字引用c primer plus) 什么是C语言 顾名思义,c语言是一门语言,但是和我们所讲的话不同,它是一门编程语言,是为了让机器可以听懂人的意思所以编写的一 ...
 - web开发,前后分离接口规范
			
1. 前言 随着互联网的高速发展,前端页面的展示.交互体验越来越灵活.炫丽,响应体验也要求越来越高,后端服务的高并发.高可用.高性能.高扩展等特性的要求也愈加苛刻,从而导致前后端研发各自专注于自己擅长 ...