其他分享
首页 > 其他分享> > Ynoi2019模拟赛

Ynoi2019模拟赛

作者:互联网

Ynoi2019模拟赛

前言

太毒瘤了!!!
感觉都是经典的问题,但以前从没想过有什么更优的办法。。
果然是我太菜了吗。。。
出题人lxl的题解
lxl的题解已经很详细了,我就直接贴代码吧。。

Yuno loves sqrt technology I

链接

P5046 Yuno loves sqrt technology I

吐槽

第一遍写完之后果不其然被卡成20分。
看了下洛谷的题解感觉有些常数应该比我做法打的方法都过了,感觉不可思议,贴了一个题解的代码发现也被卡成20.。
怀疑洛谷的测评姬变慢了。。好多题解的代码都过不了。。
然后只能自己卡常,交了5页测评。。。还好最后还是过了。。

\(Code\)

#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+10;
inline int Read(){
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x;
}
inline LL read(){
    LL x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x;
}
inline void print(LL x){
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
const int B=520;
const int T=193;
int n,m;
int a[N],bl[N],pos[N],t[N];
int pre[T+5][B+5],suf[T+5][B+5],c[T+5][B+5],sz[T+5];
int s[T+5][N];
inline void add(int x,int w){while(x<=n){t[x]+=w;x+=x&-x;} }
inline int get(int x){int re=0;while(x){re+=t[x];x-=x&-x;} return re;}
LL F[T+5][T+5];
int main(){
	n=Read();m=Read();
	for(register int i=1;i<=n;++i) {
		a[i]=Read();
		bl[i]=(i+B-1)/B;
		c[bl[i]][sz[bl[i]]++]=a[i];
		pos[a[i]]=i;
	}
	LL l,r;
	int x,y,z;
	for(register int i=1;i<=bl[n];++i){
		sort(c[i],c[i]+sz[i]);
		l=(i-1)*B+1;
		r=min(i*B,n);
		for(register int j=l,k=1;j<=r;++j,++k){
			add(a[j],1);
			pre[i][k]=k-get(a[j])+pre[i][k-1];
		}
		for(register int j=l,k=1;j<=r;++j,++k) add(a[j],-1);
		for(register int j=r,k=1;j>=l;--j,++k){
			suf[i][k]=get(a[j])+suf[i][k-1];
			add(a[j],1);
		}
		for(register int j=r,k=1;j>=l;--j,++k) add(a[j],-1);
	}
	for(register int i=1;i<bl[n];++i){
		int k=0;
		for(register int j=1;j<=n;++j){
			while(c[i][k]<j&&k<sz[i]) ++k;
			if(bl[pos[j]]<i)s[i][pos[j]]=k;
			else if(bl[pos[j]]>i) s[i][pos[j]]=B-k;
		}
		for(register int j=1;j<=n;j+=4) {
			s[i][j]+=s[i][j-1];
			s[i][j+1]+=s[i][j];
			s[i][j+2]+=s[i][j+1];
			s[i][j+3]+=s[i][j+2];
		}
	}
	for(register int i=1;i<bl[n];++i){
		for(register int j=i+1;j<bl[n];++j){
			F[i][j]=s[j][j*B]-s[j][(i-1)*B]+F[i][j-1];
		}
	}
	LL ans=0;int itl,itr;
	for(register int tt=1;tt<=m;++tt){
		l=read();r=read();
		l^=ans;r^=ans;
		if(bl[l]==bl[r]){
			x=bl[l];y=0;
			ans=pre[x][r-(x-1)*B]-pre[x][l-1-(x-1)*B];
			for(register int i=0;i<sz[x];++i){
				if(pos[c[x][i]]<l) ans-=y;
				else if(pos[c[x][i]]<=r) ++y;
			}
		}
		else{
			y=0;x=bl[l];z=bl[r];
			ans=suf[x][x*B-l+1]+pre[z][r-(z-1)*B]+F[x+1][z-1];
			itl=x*B;itr=(z-1)*B;
			for(register int i=x+1;i<z;++i)
				ans+=pre[i][B]+s[i][itl]-s[i][l-1]+s[i][r]-s[i][itr];
			for(itl=0,itr=0;itl<sz[x];++itl){
				for(;itr<sz[z]&&c[x][itl]>c[z][itr];++itr) if(pos[c[z][itr]]<=r) ++y;
				if(pos[c[x][itl]]>=l) ans+=y;
			}
		}
		print(ans);puts("");
	}
	return 0;
}

Yuno loves sqrt technology II

链接

P5047 Yuno loves sqrt technology II

吐槽

做法类似第十四分块,二次离线时要用根号平衡。。
for比while更快,然而写莫队不用while就非常蛋疼。。。
卡常过程比第一题更熟练了,只交了两页测评。。

\(Code\)

#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+10;

int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void print(LL x){
    if(x>9) print(x/10);
    putchar(x%10+'0');
}

int B;
int n,m,cnt;
int a[N],b[N],g[N],h[N];
int find(int x){
	int l=1,r=cnt,mid;
	while(1){
		mid=l+r>>1;
		if(b[mid]==x) return mid;
		else if(b[mid]<x) l=mid+1;
		else r=mid-1;
	}
}
struct query{
	int l,r,id;
}q[N];
struct node{
	int l,r,f,id,op;
};
vector<node> p[N];
bool cmpq(query x,query y){
	return g[x.l]!=g[y.l]?g[x.l]<g[y.l]:x.r<y.r;
}
int xiao[N],da[N],t[N];
inline void add(int x){
	for(;x<=n;x+=x&-x)++t[x];
}
inline int get(int x){
	int re=0;
	for(;x;x-=x&-x) re+=t[x];
	return re;
}
LL ans[N];
int main(){
	n=read();m=read();B=sqrt(n);
	for(register int i=1;i<=n;++i){
		a[i]=read();
		b[i]=a[i];
		g[i]=(i+B-1)/B;
	}
	sort(b+1,b+1+n);
	cnt=1;
	for(register int i=2;i<=n;++i) 
		if(b[i]!=b[i-1]) b[++cnt]=b[i];
	for(register int i=1;i<=n;++i) a[i]=find(a[i]);
	for(register int i=1;i<=n;++i){
		add(a[i]);
		xiao[i]=get(a[i]-1);
		da[i]=i-get(a[i]);
	}
	for(register int i=1;i<=n;++i) t[i]=0;
	for(register int i=1;i<=m;++i){
		q[i].l=read();q[i].r=read();
		q[i].id=i;
	}
	sort(q+1,q+1+m,cmpq);
	int l=1,r=0;
	for(register int i=1;i<=m;++i){
		//++r f(x,[l,r])=f(x,[1,r])-f(x,[1,l-1])
		if(r<q[i].r) p[l-1].push_back((node){r+1,q[i].r,-1,q[i].id,2});
		for(;r<q[i].r;++r) ans[q[i].id]+=da[r+1];
		//--l
		if(l>q[i].l) p[r].push_back((node){q[i].l,l-1,1,q[i].id,1});
		for(;l>q[i].l;--l) ans[q[i].id]-=xiao[l-1];
		//--r;
		if(r>q[i].r) p[l-1].push_back((node){q[i].r+1,r,1,q[i].id,2});
		for(;r>q[i].r;--r)ans[q[i].id]-=da[r];
		//++l
		if(l<q[i].l) p[r].push_back((node){l,q[i].l-1,-1,q[i].id,1});
		for(;l<q[i].l;++l)ans[q[i].id]+=xiao[l];
	}
	int v,id;
	for(register int i=1;i<=n;++i){
		xiao[i]=(a[i]-1)/B+1;
		da[i]=a[i]/B+1;
	}
	for(register int i=1;i<=n;++i){
		v=(a[i]/B);
		for(register int k=1;k<=v;++k) ++h[k];
		v=v*B+1;
		for(;v<=a[i];++v) ++t[v];
		for(register int j=0;j<p[i].size();++j){
			id=p[i][j].id;
			if(p[i][j].op==1){
				if(p[i][j].f==1) {
					ans[id]+=(LL)i*(p[i][j].r-p[i][j].l+1);
					for(register int o=p[i][j].l;o<=p[i][j].r;++o)
						ans[id]-=t[a[o]]+h[xiao[o]];
				}
				else{
					ans[id]-=(LL)i*(p[i][j].r-p[i][j].l+1);
					for(register int o=p[i][j].l;o<=p[i][j].r;++o)
						ans[id]+=t[a[o]]+h[xiao[o]];
				}
			}
			else{
				if(p[i][j].f==1) {
					for(register int o=p[i][j].l;o<=p[i][j].r;++o)
						ans[id]+=t[a[o]+1]+h[da[o]];
				}
				else{
					for(register int o=p[i][j].l;o<=p[i][j].r;++o)
						ans[id]-=h[da[o]]+t[a[o]+1];
				}
			}
		}
	}
	for(register int i=1;i<=m;++i) ans[q[i].id]+=ans[q[i-1].id];
	for(register int i=1;i<=m;++i){
		print(ans[i]);puts("");
	}
	return 0;
}

Yuno loves sqrt technology III

链接

P5048 Yuno loves sqrt technology III

吐槽

我以前也写过区间众数,前面部分想法是相同的。。
但以前在处理两边 \(O(n^0.5)\) 大小的小块时,是直接用线段树来算区间内出现次数的。。
这题这个把问题转化为是否存在出现 \(ans+1\) 次元素,就不需要真的算出一个元素的出现次数。。
感觉自己还是太菜了。。只能膜拜想出这种做法的神仙。。
实现上感觉这是三题里最不卡常的,一发就过了~

\(Code\)

#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=5e5+10;

int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void print(int x){
    if(x>9) print(x/10);
    putchar(x%10+'0');
}

int n,m,B,cnt;
int a[N],b[N],g[N],c[N],L[N],R[N];
int find(int x){
	int l=1,r=cnt,mid;
	while(1){
		mid=l+r>>1;
		if(b[mid]==x) return mid;
		else if(b[mid]<x) l=mid+1;
		else r=mid-1;
	}
}
int F[2010][2010];
int pos[N];
vector<int> v[N];
int main(){
	n=read();m=read();B=sqrt(n);
	for(int i=1;i<=n;++i) {
		a[i]=read();
		b[i]=a[i];
		g[i]=(i+B-1)/B;
	}
	for(int i=1;i<=g[n];++i){
		L[i]=(i-1)*B+1;
		R[i]=min(i*B,n);
	}
	sort(b+1,b+1+n);
	cnt=1;
	for(register int i=2;i<=n;++i) 
		if(b[i]!=b[i-1]) b[++cnt]=b[i];
	for(register int i=1;i<=n;++i) a[i]=find(a[i]);
	int mx;
	for(int i=1;i<g[n];++i){
		mx=0;
		for(int j=i;j<g[n];++j){
			for(int k=L[j];k<=R[j];++k){
				++c[a[k]];
				if(c[a[k]]>mx) mx=c[a[k]];
			}
			F[i][j]=mx;
		}
		for(int j=i;j<g[n];++j)
			for(int k=L[j];k<=R[j];++k)
				--c[a[k]];
	}
	for(int i=1;i<=n;++i){
		pos[i]=c[a[i]];
		++c[a[i]];
		v[a[i]].push_back(i);
	}
	int ans=0,l,r;
	for(int tt=1;tt<=m;++tt){
		l=read()^ans;r=read()^ans;
		ans=F[g[l]+1][g[r]-1];
		for(int i=l;i<=R[g[l]];++i){
			for(;pos[i]+ans<v[a[i]].size();++ans)
				if(v[a[i]][pos[i]+ans]>r) break;
		}
		for(int i=L[g[r]];i<=r;++i){
			for(;pos[i]-ans>=0;++ans)
				if(v[a[i]][pos[i]-ans]<l) break;
		}
		print(ans);puts("");
	}
	return 0;
}

标签:Ynoi2019,ch,10,int,mid,while,模拟,getchar
来源: https://www.cnblogs.com/Yuigahama/p/13555947.html