do_while_true

一言(ヒトコト)

「题解」洛谷 P4062 [Code+#1]Yazid 的新生舞会 线性做法

还是考虑那个众数的套路,考虑一个数 \(c\) 的贡献,将 \(c\) 置为 \(1\),不是 \(c\) 的置为 \(-1\),那么一个区间和 \(>0\) 的绝对众数是 \(c\)

从左往右和从右往左各跑一边,每次让一个 \(+1\) 匹配左边第一个没匹配的 \(-1\) (没有的话就匹配到虚空),如果一个位置从前往后或者从后往前都没匹配到,意味着它不可能和其他位置组成逆序对,这个位置就会寄掉,用并查集即可完成匹配。

跑出了这些绝对众数可能为 \(c\) 的连续段,这些连续段的总长度至多是 \(c\) 出现次数的两倍,所以所有 \(c\) 的若干连续段的总长是 \(\mathcal{O}(n)\) 的。

那么作前缀和,在每个连续段内对答案的贡献就是一个二维数点的形式。

注意到相邻数只会有 \(\pm 1\) 的改变,那么扫描线的时候,考虑计算这一个位置统计的答案和上一个位置的差值是多少即可,只需要开个桶记录一下每个值的出现次数。

如果使用线性序列并查集算法,复杂度是 \(\mathcal{O}(n)\) 的,不过写这篇题解的时候用纯路径压缩跑到了洛谷上第一。

找出极长连续段的实现上需要精细一点,可能要双指针什么的(

#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<ctime>
#include<random>
#define pb emplace_back
#define mp std::make_pair
#define fi first
#define se second
#define dbg(x) cerr<<"In Line "<< __LINE__<<" the "<<#x<<" = "<<x<<'\n';
#define dpi(x,y) cerr<<"In Line "<<__LINE__<<" the "<<#x<<" = "<<x<<" ; "<<"the "<<#y<<" = "<<y<<'\n';
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>pii;
typedef pair<ll,int>pli;
typedef pair<ll,ll>pll;
typedef vector<int>vi;
typedef vector<ll>vll;
typedef vector<pii>vpii;
inline void cmax(int &x, int y){x=x>y?x:y;}
inline void cmin(int &x, int y){x=x<y?x:y;}
#define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
inline void read(int &r){
	r=0;bool w=0;char ch=getchar();
	while(ch<'0'||ch>'9')w=ch=='-'?1:0,ch=getchar();
	while(ch>='0'&&ch<='9')r=r*10+(ch^48),ch=getchar();
	r=w?-r:r;
}
const int N=500100;
int n,type;
int a[N],L[N],R[N],visbuf[N<<1],*vis=visbuf+N;
vi vec[N];
ll ans;
int f[N],p[N];
int st[N],top;
int getfa1(int x){
	return f[x]==x?x:f[x]=getfa1(f[x]);
}
void merge1(int x,int y){
	int fx=getfa1(x),fy=getfa1(y);
	f[fy]=fx;
}
int getfa2(int x){
	return p[x]==x?x:p[x]=getfa2(p[x]);
}
void merge2(int x,int y){
	int fx=getfa2(x),fy=getfa2(y);
	p[fy]=fx;
}
signed main(){
	read(n);read(type);
	for(int i=1;i<=n;i++)read(a[i]),p[i]=f[i]=i,vec[a[i]].pb(i);
	p[n+1]=f[n+1]=n+1;
	for(int o=0;o<n;o++){
		int mn=n+1;top=0;
		for(auto i:vec[o]){
			int fa=getfa1(i-1);
			merge1(fa,i);
			if(fa)merge1(fa-1,fa);
			L[i]=fa;
			while(top&&st[top]>fa)--top;
			st[++top]=fa;
		}
		reverse(vec[o].begin(),vec[o].end());
		for(auto i:vec[o]){
			int fa=getfa2(i+1);
			merge2(fa,i);
			if(fa<=n)merge2(fa+1,fa);
			cmax(R[L[i]],fa);
		}
		reverse(vec[o].begin(),vec[o].end());
		for(int k=1;k<=top;k++){
			int l=st[k],r=R[l];
			while(l<=r){
				++l;cmax(r,R[l]);
			}
			l=st[k];cmin(r,n);
			int cnt=0,s=0;
			for(int i=l;i<=r;i++){
				L[i]=R[i]=0;f[i]=p[i]=i;
				if(i){
					if(a[i]==o)cnt+=vis[s++];
					if(a[i]!=o)cnt-=vis[--s];
				}
				++vis[s];
				ans+=cnt;
			}
			s=0;
			for(int i=l;i<=r;i++){
				if(i){
					if(a[i]==o)++s;
					if(a[i]!=o)--s;
				}
				vis[s]=0;
			}
			while(k<=top&&st[k]<=r)
				++k;
			--k;
		}
		#undef now
	}
	cout << ans << '\n';
	return 0;
}
posted @ 2022-06-30 14:41  do_while_true  阅读(27)  评论(0编辑  收藏  举报