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;
}

浙公网安备 33010602011771号