首页 > 其他分享 >CF 896 E

CF 896 E

时间:2024-06-05 15:55:42浏览次数:6  
标签:896 int ir bl CF fa il mx

link

首先,感觉这个题很难用数据结构维护,所以可以想到分块(其实也是因为数据范围 \(10^5\) 比较小)。第一个想法可能是一个块内维护每一个不同的数出现了多少次,但是发现这样减一个数的时候很难合并,没办法优化。

然后就有一个事实,就是同一个块内当一起修改的时候,相同的数也一直会相同。所以我们如果把相同的数当成一样的,每一次只改动一个数就可以了。这个可以用并查集维护,合并就是并查集的 merge。然后发现这个时候我们如果还是 \(>x\) 的数暴力修改时间复杂度还是过不去。

有一个可能的想法是我们可以第二次分块。对于不同的数和不同的 \(x\),有没有办法通过一些性质/单调性优化?有一个发现就是数的值域比较小,只有 \(10^5\)。就有了一个做法:

  • 设 \(mx\) 为当前块的最大值,\(x\) 为要减去的数。

  • 如果 \(2\cdot x\ge mx\),我们就把所有 \(>x\) 的减去 \(x\)。因此我们可以用 \(\mathcal{O}(mx-x)\) 的复杂度使最大值减少了 \(\mathcal{O}(mx-x)\)。

  • 如果 \(2\cdot x<mx\),我们就把所有 \(\le x\) 的都加上 \(x\)。这里要维护一个懒标记,以后所有的都得减 \(x\),因此我们可以用 \(\mathcal{O}(x)\) 的复杂度使最大值减少了 \(\mathcal{O}(x)\)。

因为 \(mx\) 不增,我们每一个块内的复杂度都是 \(\mathcal{O}(A)\) 的,因此这样总的复杂度就是 \(\mathcal{O}(n\sqrt{A})\) 的。

一些细节:

  • 零散块直接重构。

  • 不要像我一样 fa 数组首先写了一个 fa[B][N] 的东西。

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 1e5+5;
const int B = 320;

int n,m,bls,a[N],mx[N],v[N];
int fa[N],lb[B],rb[B],bl[N],tag[N];

struct node {
	int rt,siz;
} g[B][N];

int ff(int x){
	return x==fa[x]?x:fa[x]=ff(fa[x]);
}

void pu(int b){
	for (int i=lb[b]; i<=rb[b]; i++){
		a[i]=v[ff(i)];
		g[b][a[i]].rt=0;
		g[b][a[i]].siz=0;
		a[i]-=tag[b];
	}
	tag[b]=0;
	for (int i=lb[b]; i<=rb[b]; i++){
		fa[i]=0;
	}
}

void bd(int b){
	mx[b]=0;
	for (int i=lb[b]; i<=rb[b]; i++){
		mx[b]=max(mx[b],a[i]);
		g[b][a[i]].siz++;
		if (g[b][a[i]].rt){
			fa[i]=g[b][a[i]].rt;
		}
		else{
			v[i]=a[i];
			g[b][a[i]].rt=fa[i]=i;
		}
	}
}

void merge(int x,int a,int b){
	auto &s=g[x][a];
	auto &t=g[x][b];
	if (t.rt){
		fa[s.rt]=t.rt;
	}
	else{
		t.rt=s.rt;
		v[s.rt]=b;
	}
	t.siz+=s.siz;
	s.siz=s.rt=0;
}

void at(int b,int ad){
	if (ad<=mx[b]-tag[b]-ad){
		for (int i=tag[b]+1; i<=tag[b]+ad; i++){
			if (g[b][i].rt){
				merge(b,i,i+ad);
			}
		}
		tag[b]+=ad;
	}
	else{
		for (int i=mx[b]; i>tag[b]+ad; i--){
			merge(b,i,i-ad);
			mx[b]=min(mx[b],tag[b]+ad);
		}
	}
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin>>n>>m;
	for (int i=1; i<=n; i++){
		cin>>a[i]; 
	}
	for (int i=1; i<=n; i++){
		bl[i]=(i-1)/B+1;
	}
	bls=1;
	for (int i=1; (i-1)*B+1<=n; i++,bls++){
		lb[i]=(i-1)*B+1;
		rb[i]=min(n,i*B);
	}
	for (int i=1; i<=bls; i++){
		bd(i);
	}
	while (m--){
		int op,l,r,x;
		cin>>op>>l>>r>>x;
		if (op==1){
			int il=bl[l],ir=bl[r];
			pu(il);
			if (il!=ir){
				pu(ir);
			}
			if (il!=ir){
				for (int i=l; i<=rb[il]; i++){
					if (a[i]>x){
						a[i]-=x;
					}
				}
				for (int i=lb[ir]; i<=r; i++){
					if (a[i]>x){
						a[i]-=x;
					}
				}
				for (int i=il+1; i<ir; i++){
					at(i,x);
				}
				bd(il);
				bd(ir);
			}
			else{
				for (int i=l; i<=r; i++){
					if (a[i]>x){
						a[i]-=x;
					}
				}
				bd(il);
			}
		}
		else{
			int il=bl[l],ir=bl[r],ans=0;
			if (il!=ir){
				for (int i=l; i<=rb[il]; i++){
					if (v[ff(i)]-tag[il]==x){
						ans++;
					}
				}
				for (int i=lb[ir]; i<=r; i++){
					if (v[ff(i)]-tag[ir]==x){
						ans++;
					}
				}
				for (int i=il+1; i<ir; i++){
					if (x+tag[i]<=1e5){
						ans+=g[i][x+tag[i]].siz;
					}
				}
			}
			else{
				for (int i=l; i<=r; i++){
					if (v[ff(i)]-tag[il]==x){
						ans++;
					}
				}
			}
			cout<<ans<<"\n";
		}
	}
	return 0;
}
``

标签:896,int,ir,bl,CF,fa,il,mx
From: https://www.cnblogs.com/SFlyer/p/18233189

相关文章

  • CF941
    后面应该就先打打ACM,大二下打完退役然后直接转职网安。临近期末非常脑残,前几天放弃自我意识加班加点把堆积的事情做完了,也一点都不感到轻松因为后面还要准备沙贝期末考试。想着把市赛和月赛的题先补一下,然后两个的题解都还没update,很伤心。干脆vp一场cf换换心情把!结果vp完更伤心了......
  • CF1743D Problem with Random Tests
    题目链接:https://codeforces.com/contest/1743/problem/D这题比较考察做题的经验因为或操作对一个数的值只增不减,所以我们要往高位考虑.我们截取的第一段需要满足最高位的1在原串中也是最高位的1,这样才能做到别的所有的数都不如他大.截取的第二段需要能首先满足把第一段截取......
  • CF DP题练习
    2024.6.31997C.NikitaandLCM*1900我们令\(M=lcm(a_1,...a_n)\),则\(a\)的任何一个子序列的\(lcm\)一定是\(M\)的因数。若\(M\)大于\(a\)中的任意一个数,则答案为\(n\)。否则,我们可以以因数表示为状态,线性转移即可。状态可以存到\(set\)中利用\(map\)转移......
  • CF1572A Book
    题目链接:https://codeforces.com/problemset/problem/1572/A大致思路:题目想问的是从头到尾阅读多少次之后,才能读完这本书.这是一道很套路的拓扑排序的题.看到一个事件有前置条件这种,就应该想到建一个有向无环图然后跑拓扑排序,在这里,我们建立一条从前置条件指向当前页码的......
  • CF1980
    小号打的抽象比赛,谁知道再给我30min能不能AK?AB一眼。Cunordered_map会被卡,建议multiset。D前缀和后缀和。E发现列和行是独立的,于是对列和行分别检查。若置换矛盾,则不合法。F经过观察,一行的答案为后缀最小值-1。所以F1就能做出来了。考虑F2,对行拆贡献,维护后缀......
  • 最新区块链论文录用资讯CCF B— IWQOS 2024 共4篇
    Conference:IEEE/ACMInternationalSymposiumonQualityofService (IWQOS)CCFlevel:CCFBCategories:ComputerNetworksYear:2024Num:4Conferencetime:19-21June2024Nexus:Efficientand Conflict-EquivalentDAG-Based PermissionedBlockchainSharding......
  • CF960G Bandit Blues 题解
    我不会斯特林数。CF960GBanditBlues给你三个正整数\(n\),\(a\),\(b\),定义\(A\)为一个排列中是前缀最大值的数的个数,定义\(B\)为一个排列中是后缀最大值的数的个数,求长度为\(n\)的排列中满足\(A=a\)且\(B=b\)的排列个数。\(n\le10^5\),答案对\(998244353\)取......
  • CF1971F Circle Perimeter
    题目Givenaninteger\(r\),findthenumberoflatticepointsthathaveaEuclideandistancefrom\((0,0)\)greaterthanorequalto\(r\)butstrictlylessthan\(r+1\).Alatticepointisapointwithintegercoordinates.TheEuclideandistance......
  • 用docfx生成c#项目API的简洁教程
    1.下载docfx https://github.com/dotnet/docfx2.在环境变量的Path下面添加下载解压后docfx.exe的目录3.创建文档项目文件夹(名称如doc),位置最好是在解决方案文件夹,这样目录好配置,其它也方便。4.在doc文件夹运行cmd,或打开cmd,进入到doc文件夹5.运行docfxinit,然后根据提示......
  • 6.3 cf 944
    Problem-C-Codeforces思路分四种情况,以12为分界点(紫色部分是最初思路,但不包含所有情况)只看在a<b c<d时的图代码#include<bits/stdc++.h>usingnamespacestd;#defineIOSios::sync_with_stdio(false),cin.tie(0),cout.tie(0);intmain(){ IOS intt;......