C. 「NOIP2021模拟赛 By ZJ C」自然溢出 题解
Posted Martian148
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C. 「NOIP2021模拟赛 By ZJ C」自然溢出 题解相关的知识,希望对你有一定的参考价值。
link
solve
对于区间加一个值和减一个值我们都可以用树状数组或者线段树来维护,难点在于如何撤销操作
一次撤销操作可以看成回到某个历史节点继续操作,自然而然就想到一棵书,DFS树的同时维护线段树或者树状数组,回溯的时候反向操作就好了
code
#include<bits/stdc++.h>
#define int unsigned int
using namespace std;
const int maxn=1e6+5;
int c[2][maxn],a[maxn],N,son[maxn],lnk[maxn],nxt[maxn],cnt,M;
char s[15];
struct Q{
int op,l,r,x;
}q[maxn];
inline int read(){
int ret=0,f=1;char ch=getchar();
while(ch<\'0\'||ch>\'9\'){if(ch==\'-\')f=-f;ch=getchar();}
while(ch<=\'9\'&&ch>=\'0\')ret=ret*10+ch-\'0\',ch=getchar();
return ret*f;
}
void add(int op,int x,int y){for(;x<=N;x+=(x&-x))c[op][x]+=y;}
int get(int op,int x){int sum=0;for(;x;x-=(x&-x))sum+=c[op][x];return sum;}
int sum(int x){return x*get(0,x)-get(1,x);}
void update(int x,int y,int z){add(0,x,z),add(0,y+1,-z),add(1,x,(x-1)*z),add(1,y+1,-y*z);}
int query(int x,int y){return sum(y)-sum(x-1);}
inline int add_e(int x,int y){son[++cnt]=y;nxt[cnt]=lnk[x];lnk[x]=cnt;}
void dfs(int x){
if(q[x].op==1)update(q[x].l,q[x].r,q[x].x);
else if(q[x].op==3)q[x].x=query(q[x].l,q[x].r);
for(int j=lnk[x];j;j=nxt[j])dfs(son[j]);
if(q[x].op==1)update(q[x].l,q[x].r,-q[x].x);
}
signed main(){
N=read();M=read();
for(int i=1,last=0;i<=N;i++)a[i]=read(),add(0,i,a[i]-last),add(1,i,(i-1)*(a[i]-last)),last=a[i];
for(int i=1;i<=M;i++){
scanf("%s",s);
if(s[0]==\'A\'){
q[i].op=1;q[i].l=read();q[i].r=read();q[i].x=read();
add_e(i-1,i);
}
else if(s[0]==\'C\'){
q[i].op=2;q[i].x=read();
add_e(i-q[i].x-1,i);
}
else {
q[i].op=3;q[i].l=read();q[i].r=read();
add_e(i-1,i);
}
}
dfs(0);
for(int i=1;i<=M;i++)if(q[i].op==3){
printf("%u\\n",q[i].x);
}
return 0;
}
Noip模拟By yxj
1.random
Description
给定4个参数A0,N,c,p,你需要按下式构造A1~AN:
A[i]=(A[i-1]2+c)mod p
之后,你需要求出A1~AN中,第K大的数值。
Input
一行五个正整数A0,N,c,p,K。
Output
一行一个整数,描述答案。
Sample Input
123 10 435 3451 5
Sample Output
2936
Range
测试点 范围
1~3 K<=N<=105
4~10 K<=N<=5*106
直接stl
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long LL;
#define N 5000010
LL n,c,p,k;
LL a[N];
bool cmp(const int & a,const int & b)
{
return a>b;
}
int main()
{
freopen("random.in","r",stdin);freopen("random.out","w",stdout);
scanf("%lld%lld%lld%lld%lld",&a[0],&n,&c,&p,&k);
for (LL i=1;i<=n;i++)
a[i]=((a[i-1]*a[i-1])%p+c%p)%p;
nth_element(a+1,a+k,a+n+1,cmp);
printf("%I64d",a[k]);
return 0;
}
2.sequence
Description
给一个长度为n的序列a,以及q个询问。每次询问给出两个参数l,r,你需要输出子 序列al~ar的最大连续字段和。
Input
第一行两个正整数n,q。
接下来一行n个整数,描述序列a。
接下来q行,每行两个参数l,r,描述一个询问。
Output
对于每个询问,输出一行一个整数,描述答案。
Sample Input
4 3
1 -2 3 2
1 4
1 2
2 2
Sample Output
5
1
0
Range
测试点 范围
1~3 N,Q<=1000
4~7 N,Q<=105
8~10 N<=105,Q<=106
线段树维护。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout);
#define maxn 100010
#define IN inline
#define RE register
using namespace std;
typedef long long llg;
int n,m,zhi[maxn*3],now=maxn*3-2,ll,rr,D;
int sumv[maxn*3],le[maxn*3],ri[maxn*3];
IN int getint(){
int w=0,q=0;
char c=getchar();
while((c<‘0‘||c>‘9‘)&&c!=‘-‘) c=getchar();
if(c==‘-‘) q=1,c=getchar();
while(c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar();
return q?-w:w;
}
IN void update(int u,int lc,int lv){
sumv[u]=sumv[lc]+sumv[lv];
le[u]=max(le[lc],sumv[lc]+le[lv]);
ri[u]=max(ri[lv],sumv[lv]+ri[lc]);
zhi[u]=max(zhi[lc],zhi[lv]);
zhi[u]=max(zhi[u],ri[lc]+le[lv]);
}
IN void build(int u,int l,int r){
int lc=u<<1,lv=u<<1|1,mid=l+r>>1;
if(l==r){
sumv[u]=getint();
if(sumv[u]>0) zhi[u]=le[u]=ri[u]=sumv[u];
return;
}
build(lc,l,mid);
build(lv,mid+1,r);
update(u,lc,lv);
}
IN void query(int u,int l,int r){
int lc=u<<1,lv=u<<1|1,mid=l+r>>1;
if(l>=ll && r<=rr){
if(!D){
zhi[now]=zhi[u];sumv[now]=sumv[u];
le[now]=le[u];ri[now]=ri[u];D++;
}
else{
now^=1;
update(now,now^1,u);
}
return;
}
if(ll<=mid) query(lc,l,mid);
if(rr>mid) query(lv,mid+1,r);
}
int main(){
File("sequence");
n=getint();m=getint();
build(1,1,n);
while(m--){
ll=getint();rr=getint();
D=0;query(1,1,n);
printf("%d\n",zhi[now]);
}
}
3.tree
Description
给一棵n个节点的无根树,求路径长度=K的简单路径数。
Input
第一行两个正整数n,K。
接下来n-1行,每行两个正整数x,y,描述一条边(x,y)。
Output
一行一个整数,描述答案。
Sample Input
4 2
1 2
2 3
2 4
Sample Output
3
Range
测试点 范围
1~3 2<=K<=N<=1000
4~10 2<=K<=N<=105
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout);
#define maxn 100010
using namespace std;
typedef long long llg;
int head[maxn],next[maxn<<1],to[maxn<<1],tt,n,ans,D;
int a[maxn],c1[maxn],c2[maxn],siz[maxn],val[maxn];
bool w[maxn];
void link(){
int x,y;
scanf("%d%d",&x,&y);
to[++tt]=y;next[tt]=head[x];head[x]=tt;
to[++tt]=x;next[tt]=head[y];head[y]=tt;
}
void dfs1(int u,int fa){
siz[u]=1;a[++tt]=u;val[u]=0;
for(int i=head[u],v;v=to[i],i;i=next[i])
if(v!=fa && !w[v]){
dfs1(v,u);siz[u]+=siz[v];
val[u]=max(val[u],siz[v]);
}
}
void dfs2(int u,int fa,int now){
c1[now]++;if(now>=D) return;
for(int i=head[u],v;v=to[i],i;i=next[i])
if(v!=fa && !w[v]) dfs2(v,u,now+1);
}
void solve(int u){
int x=u;tt=0;
dfs1(u,0);
for(int i=2;i<=tt;i++){
val[a[i]]=max(val[a[i]],siz[u]-siz[a[i]]);
if(val[a[i]]<val[x]) x=a[i];
}
w[x]=1;
for(int i=head[x],v;v=to[i],i;i=next[i])
if(!w[v]){
dfs2(v,0,1);ans+=c1[D];
for(int j=1;c1[j];j++) ans+=c2[D-j]*c1[j];
for(int j=1;c1[j];j++) c2[j]+=c1[j],c1[j]=0;
}
for(int i=1;c2[i];i++) c2[i]=0;
for(int i=head[x],v;v=to[i],i;i=next[i])
if(!w[v]) solve(v);
}
int main(){
File("tree");
scanf("%d%d",&n,&D);
for(int i=1;i<n;i++) link();
solve(1);
printf("%d",ans);
}
以上是关于C. 「NOIP2021模拟赛 By ZJ C」自然溢出 题解的主要内容,如果未能解决你的问题,请参考以下文章
2014-5-10 NOIP模拟赛 by coolyangzc
[SinGuLaRiTy] NOIP模拟题 by liu_runda