【根号分治】 通知

通知

在这里插入图片描述


分析:

这道题根号分治看起来就没有前面几题那么明显了
emm当然也可能是我境界还没到
我们考虑如果暴力修改,复杂度是 O ( n m ) O(nm) O(nm),其实m为这个点的度数
考虑根号分治的思想,我们令 m = M m=\sqrt M m=M
并命度数大于m的点为关键点。
显然整张图最多只有 M \sqrt M M 个关键点。
对于暴力修改而言,关键点的复杂度是较高的,而非关键点暴力修改的复杂度可以接受。
所以对于一个修改,如果当前点是非关键点,我们就暴力修改周围的点
如果当前点是关键点,我们就打个标记,表示当前关键点又一次修改
那么查询一个点的贡献,就是当前点的直接贡献以及周围关键点的贡献
由于关键点个数最多只有 O ( M ) O(\sqrt M) O(M )
所以查询的复杂度也最多是 O ( M ) O(\sqrt M) O(M )
总的复杂度就是 O ( n M ) O(n\sqrt M) O(nM )
有一点需要注意的是,如果当前点想清空,由于当前点的贡献是直接贡献加关键点贡献,所以我们应该是让直接贡献变为-关键点贡献,这样才能将全部周围的点的贡献都清空。


#include<bits/stdc++.h>
using namespace std;

const int N = 2e5+100;
int n,m;
vector < int > a[N],b[N];
#define pb push_back
int q,du[N];
bool vi[N];
int cnt[N],cntb[N];

int main(){
	cin.tie(0);
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for (int i = 1,x,y; i <= m; i++)
	  cin>>x>>y,a[x].pb(y),a[y].pb(x),du[x]++,du[y]++;
	int div = sqrt(m);
	for (int i = 1; i <= n; i++)
	  if (du[i] >= div) vi[i] = 1;
	for (int i = 1; i <= n; i++)
	  for (int j = 0; j < a[i].size(); j++)
	    if (vi[a[i][j]]) b[i].pb(a[i][j]);
	cin>>q;
	while (q--){
		int op , x; cin>>op>>x;
		if (op == 1){
			if (vi[x]) cntb[x]++;
			else{
				for (int i = 0; i < a[x].size(); i++)
				  cnt[a[x][i]]++;
			}
			continue;
		}
		int cntt = 0;
		for (int i = 0; i < b[x].size(); i++)
		  cntt+=cntb[b[x][i]];
		cout<<cnt[x]+cntt<<endl;
		cnt[x] = -cntt;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值