Ynoi 选做

[Ynoi Easy Round 2023] TEST_69

考虑类似花神游历各国的想法,一个值只会被有效操作 \(O(\log V)\) 次,考虑用线段树维护区间,问题在于快速判断一个区间 \([l,r]\) 是否全部无法进行有效操作,此时必然满足 \(a_i \mid x\),对于区间则是 \(\operatorname{lcm}\limits_{i=l}^{r} a_i \mid x\),当区间 \(\operatorname{lcm}\) 过大时显然不满足条件,我们只需要维护区间 \(\operatorname{lcm}\) 即可。

复杂度不太会分析,反正能过。

#include<iostream>
#include<cstdio>
using namespace std;
const long long INF=1145141919810114514;
long long my_gcd(long long num1,long long num2){
	while(num2){
		long long num3=num1%num2;
		num1=num2;
		num2=num3; 
	}
	return num1;
}
long long my_lcm(long long num1,long long num2){
	if(num1==-1  ||  num2==-1){
		return -1;
	}
	else{
		long long num3=my_gcd(num1,num2);
		if(num1/num3>INF/num2){
			return -1;
		}
		else{
			return num1/num3*num2;
		}
	}
}
long long num[200010];
struct Node{
	int l,r;
	long long lcm;
	unsigned int sum;
}a[800010];
void pushup(int id){
	a[id].sum=a[id*2].sum+a[id*2+1].sum;
	a[id].lcm=my_lcm(a[id*2].lcm,a[id*2+1].lcm);
}
void build(int id,int l,int r){
	a[id].l=l;
	a[id].r=r;
	if(l==r){
		a[id].lcm=num[l];
		a[id].sum=a[id].lcm;
	}
	else{
		int mid=(l+r)>>1;
		build(id*2,l,mid);
		build(id*2+1,mid+1,r);
		pushup(id);
	}
}
void modify(int id,int l,int r,long long dif){
	if(a[id].lcm!=-1  &&  dif%a[id].lcm==0){
		return ;
	}
	if(a[id].l==a[id].r){
		a[id].lcm=my_gcd(a[id].lcm,dif);
		a[id].sum=a[id].lcm;
		return ;
	}
	if(l<=a[id*2].r){
		modify(id*2,l,r,dif);
	}
	if(a[id*2+1].l<=r){
		modify(id*2+1,l,r,dif);
	}
	pushup(id);
}
unsigned int query(int id,int l,int r){
	if(l<=a[id].l  &&  a[id].r<=r){
		return a[id].sum;
	}
	unsigned int ans=0;
	if(l<=a[id*2].r){
		ans+=query(id*2,l,r);
	}
	if(a[id*2+1].l<=r){
		ans+=query(id*2+1,l,r);
	}
	return ans;
}
int main(){
	int n,m;
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&num[i]); 
	}
	build(1,1,n);
	while(m--){
		int cmd;
		scanf("%d",&cmd);
		if(cmd==1){
			int l,r;
			long long dif;
			scanf("%d %d %lld",&l,&r,&dif);
			modify(1,l,r,dif);
		}
		else{
			int l,r;
			scanf("%d %d",&l,&r);
			printf("%u\n",query(1,l,r));
		}
	}
	return 0;
}

[Ynoi1999] XM66F

简单莫队,我们只需要考虑从 \((l,r)\) 转移到 \((l,r+1)\) 时如何处理,发现此时增加的贡献为 \(\sum\limits_{i=l}^{r} [a_i=a_{r+1}] \sum\limits_{j=i+1}^{r} [a_j<a_i]\),拆开即有 \(\sum\limits_{i=l}^{r} [a_i=a_{r+1}] \sum\limits_{j=i+1}^{r} [a_j<a_i]\),可以化为 \(\sum\limits_{i=l}^{r} [a_i=a_{r+1}] (\sum\limits_{j=1}^{r} [a_j<a_i] - \sum\limits_{j=1}^{i} [a_j<a_i])\)。其中 \(\sum\limits_{j=1}^{r} [a_j<a_i]\) 可以看作 \(\sum\limits_{j=1}^{r} [a_j<a_{r+1}]\),更激进一点可以写成 \(\sum\limits_{j=1}^{r+1} [a_j<a_{r+1}]\)。我们记 \(f(x)=\sum\limits_{j=1}^{x} [a_j < a_x]\),则原式可以改成 \(\sum\limits_{i=l}^{r} [a_i=a_{r+1}] (f(r+1)-f(i))\)。预处理出 \(f\) 即可。

时间复杂度 \(O(n \log n + n \sqrt{n})\)

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int B=700;
int n,m,a[500010];
struct Query{
	int l,r,id;
}query[500010];
bool operator <(const Query &lhs,const Query &rhs){
	if(lhs.l/B!=rhs.l/B){
		return lhs.l/B<rhs.l/B;
	}
	else{
		return lhs.r/B<rhs.r/B;
	}
}
long long f[500010],ans[500010],preans;
long long c[500010];
void modify(int pos){
	while(pos<=n){
		c[pos]++;
		pos+=pos&-pos;
	}
}
long long ask(int pos){
	long long sum=0;
	while(pos>=1){
		sum+=c[pos];
		pos-=pos&-pos;
	}
	return sum;
}
void init(){
	for(int i=1;i<=n;i++){
		modify(a[i]);
		f[i]=ask(a[i]-1);
	}
}
long long sum1[500010],sum2[500010];
int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]); 
	}
	for(int i=1;i<=m;i++){
		scanf("%d %d",&query[i].l,&query[i].r);
		query[i].id=i;
	}
	sort(query+1,query+m+1);
	init();
	int prel=1,prer=0;
	for(int i=1;i<=m;i++){
		int l=query[i].l,r=query[i].r,id=query[i].id;
		while(prer<r){
			prer++;
			preans+=sum1[a[prer]]*f[prer]-sum2[a[prer]];
			sum1[a[prer]]++;
			sum2[a[prer]]+=f[prer];
		}
		while(prel>l){
			prel--;
			preans+=sum2[a[prel]]-sum1[a[prel]]*f[prel];
			sum1[a[prel]]++;
			sum2[a[prel]]+=f[prel];
		}
		while(prer>r){
			sum1[a[prer]]--;
			sum2[a[prer]]-=f[prer];
			preans-=sum1[a[prer]]*f[prer]-sum2[a[prer]];
			prer--;
		}
		while(prel<l){
			sum1[a[prel]]--;
			sum2[a[prel]]-=f[prel];
			preans-=sum2[a[prel]]-sum1[a[prel]]*f[prel];
			prel++;
		}
		ans[id]=preans;
	}
	for(int i=1;i<=m;i++){
		printf("%lld\n",ans[i]);
	}
	return 0;
}

[Ynoi Easy Round 2024] TEST_132(目前卡常)

根号分治。记横坐标为 \(x\) 的点有 \(cnt_x\) 个,若 \(x \leq \sqrt{n}\) 则暴力修改,若 \(x > \sqrt{n}\) 打修改标记,在查询时带上标记即可。

实现时需要求 \(v_i^{2^t}\) 状物,所以还需要带上快速幂的复杂度,需要平衡块长。

复杂度大概是 \(O(n \sqrt{n \log V})\) 的。

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
namespace FAST_IO{const int BUF(1<<20);char buf[BUF],*p1=buf,*p2=buf;char pbuf[BUF],*p=pbuf;char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,BUF,stdin),p1==p2)?EOF:*p1++;}void pc(char c){*p++=c;if(p-pbuf==BUF)fwrite(pbuf,1,BUF,stdout),p=pbuf;}void flush(){fwrite(pbuf,1,p-pbuf,stdout);p=pbuf;}template<typename T>void read(T&x){x=0;static char c;T f=1;do{c=gc();if(c=='-')f=-f;}while(!isdigit(c));do{x=(x<<3)+(x<<1)+(c^48);c=gc();}while(isdigit(c));x*=f;}template<typename T,typename...Args>void read(T&x,Args&...args){read(x);read(args...);}template<typename T>void write(T x){if(x<0){pc('-');x=-x;}static char stk[1<<8],*tp;tp=stk;do*tp++=(x%10)^48;while(x/=10);while(tp!=stk)pc(*--tp);}void write(char c){pc(c);}template<typename T,typename...Args>void write(T x,Args...args){write(x);write(args...);}struct TMP{~TMP(){flush();}}tmp;};
using namespace FAST_IO;
const int N=1200000,B=3100,C=30;
const long long mod=1000000007;
int x[N+10],y[N+10],cnt_dot[N+10];
long long v[N+10],pre_ans[N+10],pow_num[N+10];
bool huge[N+10];
vector<int> huge_dot[N+10],light_dot[N+10];
long long pow_val[N+10][C]; 
long long pow_mod(int id,long long num2){
	long long ans=1;
	while(num2){
		ans*=pow_val[id][__builtin_ctz(num2)];
		ans%=mod;
		num2-=num2&-num2;
	}
	return ans;
}
int main(){
	int n,m;
	read(n,m);
	for(int i=1;i<=n;i++){
		read(x[i],y[i],v[i]);
		cnt_dot[x[i]]++;
		pow_val[i][0]=v[i];
		for(int j=1;j<C;j++){
			pow_val[i][j]=pow_val[i][j-1]*pow_val[i][j-1]%mod;
		}
	}
	for(int i=1;i<=n;i++){
		if(cnt_dot[i]>B){
			huge[i]=true;
			pow_num[i]=1;
		}
	}
	for(int i=1;i<=n;i++){
		if(huge[x[i]]){
			huge_dot[y[i]].push_back(i);
		}
		else{
			light_dot[x[i]].push_back(i);
			pre_ans[y[i]]+=v[i];
		}
	}
	while(m--){
		int cmd,num;
		read(cmd,num);
		if(cmd==1){
			if(huge[num]){
				pow_num[num]*=2;
				pow_num[num]%=mod-1;
			}
			else{
				for(int i=0;i<light_dot[num].size();i++){
					int pre=light_dot[num][i];
					pre_ans[y[pre]]-=v[pre];
					v[pre]*=v[pre];
					v[pre]%=mod;
					pre_ans[y[pre]]+=v[pre];
				}
			}
		}
		else{
			long long ans=pre_ans[num];
			for(int i=0;i<huge_dot[num].size();i++){
				int pre=huge_dot[num][i];
				ans+=pow_mod(pre,pow_num[x[pre]]);
			}
			write(ans%mod);
			pc('\n');
		}
	}
	return 0;
}

[Ynoi2011] 初始化(目前卡常)

\(x > \sqrt{n}\) 时,被修改的位置只有 \(O(\sqrt{n})\) 个,暴力处理,用分块维护即可。

\(x \leq \sqrt{n}\) 时,由于题目给出的形式较为简单,可以认为是把下标模 \(x\)\(y\) 同余的位置加上 \(z\)。我们对每个模数分别处理,用分块平衡复杂度即可。

认为 \(n\)\(m\) 同阶,复杂度是 \(O(n \sqrt{n})\) 的。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int mod=1000000007;
const int N=200000,B=160;
int block_id[N+10],block_l[1510],block_r[1510],block_cnt;
int a[N+10],block_sum[1510];
int pre_sum[1510][1510];
namespace FAST_IO{const int BUF(1<<20);char buf[BUF],*p1=buf,*p2=buf;char pbuf[BUF],*p=pbuf;char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,BUF,stdin),p1==p2)?EOF:*p1++;}void pc(char c){*p++=c;if(p-pbuf==BUF)fwrite(pbuf,1,BUF,stdout),p=pbuf;}void flush(){fwrite(pbuf,1,p-pbuf,stdout);p=pbuf;}template<typename T>void read(T&x){x=0;static char c;T f=1;do{c=gc();if(c=='-')f=-f;}while(!isdigit(c));do{x=(x<<3)+(x<<1)+(c^48);c=gc();}while(isdigit(c));x*=f;}template<typename T,typename...Args>void read(T&x,Args&...args){read(x);read(args...);}template<typename T>void write(T x){if(x<0){pc('-');x=-x;}static char stk[1<<8],*tp;tp=stk;do*tp++=(x%10)^48;while(x/=10);while(tp!=stk)pc(*--tp);}void write(char c){pc(c);}template<typename T,typename...Args>void write(T x,Args...args){write(x);write(args...);}struct TMP{~TMP(){flush();}}tmp;};
using namespace FAST_IO;
int main(){
	memset(block_l,0x3f,sizeof(block_l));
	memset(block_r,-0x3f,sizeof(block_r));
	int n,m;
	read(n,m);
	for(int i=1;i<=n;i++){
		read(a[i]);
		a[i]%=mod;
		block_id[i]=(i-1)/B+1;
		block_cnt=max(block_cnt,block_id[i]);
		block_l[block_id[i]]=min(block_l[block_id[i]],i);
		block_r[block_id[i]]=max(block_r[block_id[i]],i);
		block_sum[block_id[i]]+=a[i];
		block_sum[block_id[i]]%=mod;
	}
	while(m--){
		int cmd;
		read(cmd);
		if(cmd==1){
			int x,y,z;
			read(x,y,z);
			if(x<=B){
				for(int i=y;i<=x;i++){
					pre_sum[x][i]+=z;
					pre_sum[x][i]%=mod;
				}
			}
			else{
				for(int i=y;i<=n;i+=x){
					a[i]+=z;
					a[i]%=mod;
					block_sum[block_id[i]]+=z;
					block_sum[block_id[i]]%=mod;
				}
			}
		}
		else{
			int l,r;
			read(l,r);
			int ans=0;
			if(block_id[l]==block_id[r]){
				for(int i=l;i<=r;i++){
					ans+=a[i];
					ans%=mod;
				}
			}
			else{
				for(int i=l;i<=block_r[block_id[l]];i++){
					ans+=a[i];
					ans%=mod;
				}
				for(int i=block_l[block_id[r]];i<=r;i++){
					ans+=a[i];
					ans%=mod;
				}
				for(int i=block_id[l]+1;i<=block_id[r]-1;i++){
					ans+=block_sum[i];
					ans%=mod;
				}
			}
			for(int i=1;i<=B;i++){
				int b_id_l=(l-1)/i+1,b_id_r=(r-1)/i+1;
				int b_ord_l=(l-1)%i+1,b_ord_r=(r-1)%i+1;
				if(b_id_l==b_id_r){
					ans+=pre_sum[i][b_ord_r]-pre_sum[i][b_ord_l-1];
					ans=(ans%mod+mod)%mod;
				}
				else{
					ans+=pre_sum[i][i]-pre_sum[i][b_ord_l-1];
					ans=(ans%mod+mod)%mod;
					ans+=pre_sum[i][b_ord_r];
					ans%=mod;
					ans+=(long long)pre_sum[i][i]*(b_id_r-b_id_l-1)%mod;
					ans%=mod; 
				}
			}
			write(ans);
			pc('\n');
		}
	}
	return 0;
}

[Ynoi April Fool's Round 2025] 牢夸

首先我们证明 \(R-L \leq 2\)。容易发现当 \(R-L > 2\) 时,区间长度 \(\geq 4\),可以拆分成两个长度 \(\geq 2\) 的区间,这两个区间中必然存在一个区间,使得其平均值不小于原区间平均值,取该区间不劣。

接下来使用线段树维护即可,复杂度 \(O(n \log n)\)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const long long INF=-1145141919810;
long long num[1000010];
struct Node{
	int l,r;
	long long tag,max_2,max_3,l_1,l_2,r_1,r_2;
}a[4000010];
Node merge(Node lhs,Node rhs){
	Node hs;
	hs.l=lhs.l;
	hs.r=rhs.r;
	hs.tag=0;
	hs.max_2=max(lhs.r_1+rhs.l_1,max(lhs.max_2,rhs.max_2));
	hs.max_3=max(max(lhs.r_2+rhs.l_1,lhs.r_1+rhs.l_2),max(lhs.max_3,rhs.max_3));
	if(hs.r-hs.l+1<3){
		hs.max_3=INF;
	}
	hs.l_1=lhs.l_1;
	if(lhs.l==lhs.r){
		hs.l_2=lhs.l_1+rhs.l_1;
	}
	else{
		hs.l_2=lhs.l_2;
	}
	hs.r_1=rhs.r_1;
	if(rhs.l==rhs.r){
		hs.r_2=rhs.r_1+lhs.r_1;
	}
	else{
		hs.r_2=rhs.r_2;
	}
	return hs;
}
void build(int id,int l,int r){
	if(l==r){
		a[id].l=l;
		a[id].r=r;
		a[id].tag=0;
		a[id].max_2=INF;
		a[id].max_3=INF;
		a[id].l_1=num[l];
		a[id].l_2=INF;
		a[id].r_1=num[r];
		a[id].r_2=INF; 
	}
	else{
		int mid=(l+r)>>1;
		build(id*2,l,mid);
		build(id*2+1,mid+1,r);
		a[id]=merge(a[id*2],a[id*2+1]);
	}
}
void pushdown(int id){
	long long dif=a[id].tag;
	a[id*2].tag+=dif;
	if(a[id*2].max_2!=INF){
		a[id*2].max_2+=dif*2;
	}
	if(a[id*2].max_3!=INF){
		a[id*2].max_3+=dif*3;
	}
	a[id*2].l_1+=dif;
	if(a[id*2].l_2!=INF){
		a[id*2].l_2+=dif*2;
	}
	a[id*2].r_1+=dif;
	if(a[id*2].r_2!=INF){
		a[id*2].r_2+=dif*2; 
	}
	a[id*2+1].tag+=dif;
	if(a[id*2+1].max_2!=INF){
		a[id*2+1].max_2+=dif*2;
	}
	if(a[id*2+1].max_3!=INF){
		a[id*2+1].max_3+=dif*3;
	}
	a[id*2+1].l_1+=dif;
	if(a[id*2+1].l_2!=INF){
		a[id*2+1].l_2+=dif*2;
	}
	a[id*2+1].r_1+=dif;
	if(a[id*2+1].r_2!=INF){
		a[id*2+1].r_2+=dif*2; 
	}
	a[id].tag=0;
}
void modify(int id,int l,int r,long long dif){
	if(l<=a[id].l  &&  a[id].r<=r){
		a[id].tag+=dif;
		if(a[id].max_2!=INF){
			a[id].max_2+=dif*2;
		}
		if(a[id].max_3!=INF){
			a[id].max_3+=dif*3;
		}
		a[id].l_1+=dif;
		if(a[id].l_2!=INF){
			a[id].l_2+=dif*2;
		}
		a[id].r_1+=dif;
		if(a[id].r_2!=INF){
			a[id].r_2+=dif*2; 
		}
		return ;
	}
	pushdown(id);
	if(l<=a[id*2].r){
		modify(id*2,l,r,dif);
	}
	if(a[id*2+1].l<=r){
		modify(id*2+1,l,r,dif);
	}
	a[id]=merge(a[id*2],a[id*2+1]);
}
Node query(int id,int l,int r){
	if(l<=a[id].l  &&  a[id].r<=r){
		return a[id];
	}
	pushdown(id);
	Node ansl,ansr;
	bool flagl=false,flagr=false;
	if(l<=a[id*2].r){
		flagl=true;
		ansl=query(id*2,l,r);
	}
	if(a[id*2+1].l<=r){
		flagr=true;
		ansr=query(id*2+1,l,r);
	}
	if(flagl  &&  flagr){
		return merge(ansl,ansr);
	}
	else if(flagl){
		return ansl;
	}
	else{
		return ansr;
	}
}
int main(){
	int n,m;
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&num[i]);
	}
	build(1,1,n);
	while(m--){
		int cmd;
		scanf("%d",&cmd);
		if(cmd==1){
			int l,r;
			long long dif;
			scanf("%d %d %lld",&l,&r,&dif);
			modify(1,l,r,dif);
		}
		else{
			int l,r;
			scanf("%d %d",&l,&r);
			Node ans=query(1,l,r);
			if(ans.max_2*3>=ans.max_3*2){
				long long lhs=ans.max_2,rhs=2;
				if(lhs%rhs==0){
					lhs/=rhs;
					rhs/=rhs;
				}
				printf("%lld/%lld\n",lhs,rhs);
			}
			else{
				long long lhs=ans.max_3,rhs=3;
				if(lhs%rhs==0){
					lhs/=rhs;
					rhs/=rhs;
				}
				printf("%lld/%lld\n",lhs,rhs);
			}
		}
	}
	return 0;
}
posted @ 2025-12-02 15:59  Oken喵~  阅读(2)  评论(0)    收藏  举报