2023.1.13模拟赛总结
实质上是阶段性学习检测。
T1:线段树
直接建一棵区间为1~m的线段树,每插入一点就将该点值修改,维护区间最大值。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define MAXN 200005
#define INF INT_MAX
LL m,d,n;
struct node{
LL l,r,maxn;
}tr[MAXN*4];
void pushup(LL rt){
if(tr[rt].l!=tr[rt].r){
tr[rt].maxn=max(tr[rt<<1].maxn,tr[rt<<1|1].maxn);
}
}
void build(LL rt,LL l,LL r){
tr[rt].l=l;tr[rt].r=r;
if(l==r){
tr[rt].maxn=0;
return ;
}
LL mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
void update(LL rt,LL l,LL r,LL val){
if(tr[rt].l>r||tr[rt].r<l){
return ;
}else if(tr[rt].l>=l&&tr[rt].r<=r){
tr[rt].maxn=max(tr[rt].maxn,val);
return ;
}else{
update(rt<<1,l,r,val);
update(rt<<1|1,l,r,val);
pushup(rt);
}
}
LL query(LL rt,LL l,LL r){
if(tr[rt].l>r||tr[rt].r<l)return -INF;
else if(tr[rt].l>=l&&tr[rt].r<=r){
pushup(rt);
return tr[rt].maxn;
}else{
pushup(rt);
return max(query(rt<<1,l,r),query(rt<<1|1,l,r));
}
}
int main(){
scanf("%lld%lld",&m,&d);
LL t=0;
build(1,1,m);
while(m--){
char ch;
while(1){
scanf("%c",&ch);
if(ch=='A'||ch=='Q')break;
}
if(ch=='Q'){
LL l;
scanf("%lld",&l);
t=query(1,n-l+1,n);
printf("%lld\n",t);
}else if(ch=='A'){
LL x;
scanf("%lld",&x);
x=(x+t)%d;
n++;
update(1,n,n,x);
}
}
}
T2:树链剖分
经典树剖,修改路径上的点,统计子树总和(此处注意子树节点实质上编号是在一起的,可以线段树区间修改)。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define MAXN 1000005
#define INF INT_MAX
LL n,q;
vector<LL>a[MAXN];
void add(LL u,LL v){
a[u].push_back(v);
}
struct node{
LL l,r,val,lazy;
}tr[MAXN*4];
LL siz[MAXN],fa[MAXN],son[MAXN],dep[MAXN];
void dfs1(LL x){
siz[x]=1;dep[x]=dep[fa[x]]+1;
for(LL i=0;i<a[x].size();i++){
LL xx=a[x][i];
if(xx==fa[x])continue;
fa[xx]=x;
dfs1(xx);
siz[x]+=siz[xx];
if(siz[xx]>siz[son[x]])son[x]=xx;
}
}
LL top[MAXN],p[MAXN],fp[MAXN],pos;
void dfs2(LL x,LL xtop){
top[x]=xtop;p[x]=++pos;fp[pos]=x;
if(son[x])dfs2(son[x],xtop);
for(LL i=0;i<a[x].size();i++){
LL xx=a[x][i];
if(xx==son[x]||xx==fa[x])continue;
dfs2(xx,xx);
}
}
void build(LL rt,LL l,LL r){
tr[rt].l=l;tr[rt].r=r;tr[rt].val=0;
if(l==r){
return ;
}
LL mid=(l+r)>>1;
build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);
}
void pushup(LL rt){
if(tr[rt].l!=tr[rt].r){
tr[rt].val=tr[rt<<1].val+tr[rt<<1|1].val;
}
}
void pushdown(LL rt){
if(tr[rt].lazy){
tr[rt<<1].lazy+=tr[rt].lazy;
tr[rt<<1|1].lazy+=tr[rt].lazy;
tr[rt<<1].val+=tr[rt].lazy*(tr[rt<<1].r-tr[rt<<1].l+1);
tr[rt<<1|1].val+=tr[rt].lazy*(tr[rt<<1|1].r-tr[rt<<1|1].l+1);
tr[rt].lazy=0;
}
}
void update_(LL rt,LL l,LL r,LL d){
pushdown(rt);
if(tr[rt].l>r||tr[rt].r<l)return ;
else if(tr[rt].l>=l&&tr[rt].r<=r){
tr[rt].val+=d*(tr[rt].r-tr[rt].l+1);tr[rt].lazy+=d;
return ;
}else{
update_(rt<<1,l,r,d);
update_(rt<<1|1,l,r,d);
pushup(rt);
}
}
void update(LL u,LL v,LL d){
LL f1=top[u],f2=top[v];
while(f1!=f2){
if(dep[f1]<dep[f2]){
swap(f1,f2);swap(u,v);
}
update_(1,p[f1],p[u],d);
u=fa[f1];f1=top[u];
}
if(dep[u]>dep[v])swap(u,v);
update_(1,p[u],p[v],d);
return ;
}
LL query(LL rt,LL l,LL r){
pushdown(rt);pushup(rt);
if(tr[rt].l>r||tr[rt].r<l)return 0;
else if(tr[rt].l>=l&&tr[rt].r<=r){
return tr[rt].val;
}else{
return query(rt<<1,l,r)+query(rt<<1|1,l,r);
}
}
int main(){
scanf("%lld",&n);
for(LL i=1;i<n;i++){
LL u,v;
scanf("%lld%lld",&u,&v);
u++;v++;
add(u,v);
}
dfs1(1);dfs2(1,1);build(1,1,n);
scanf("%lld",&q);
while(q--){
char ch[15];
scanf("%s",ch);
if(ch[0]=='A'){
LL u,v,d;
scanf("%lld%lld%lld",&u,&v,&d);
u++;v++;
update(u,v,d);
}else if(ch[0]=='Q'){
LL u;
scanf("%lld",&u);
u++;
printf("%lld\n",query(1,p[u],p[u]+siz[u]-1));
}
}
}
T3:线段树
经典操作,维护区间最大连续子段和,单点修改。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define MAXN 500005
#define INF INT_MAX
struct node{
LL l,r,val;
LL lson,rson,maxn;
}tr[MAXN*4];
LL n,m;
LL a[MAXN];
void pushup(LL rt){
if(tr[rt].l!=tr[rt].r){
tr[rt].val=tr[rt<<1].val+tr[rt<<1|1].val;
tr[rt].lson=max(tr[rt<<1].lson,tr[rt<<1].val+tr[rt<<1|1].lson);
tr[rt].rson=max(tr[rt<<1|1].rson,tr[rt<<1|1].val+tr[rt<<1].rson);
tr[rt].maxn=max(tr[rt<<1].maxn,tr[rt<<1|1].maxn);
tr[rt].maxn=max(tr[rt].maxn,tr[rt<<1].rson+tr[rt<<1|1].lson);
tr[rt].maxn=max(tr[rt].maxn,max(tr[rt].lson,tr[rt].rson));
}
}
void build(LL rt,LL l,LL r){
tr[rt].l=l;tr[rt].r=r;
if(l==r){
tr[rt].val=a[l];
tr[rt].lson=tr[rt].rson=a[l];
tr[rt].maxn=a[l];
return ;
}
LL mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
void update(LL rt,LL p,LL s){
if(tr[rt].l==p&&tr[rt].r==p){
tr[rt].val=s;
tr[rt].lson=tr[rt].rson=s;
tr[rt].maxn=s;
return ;
}else{
LL mid=(tr[rt].l+tr[rt].r)>>1;
if(p>mid)update(rt<<1|1,p,s);
else if(p<=mid)update(rt<<1,p,s);
pushup(rt);
}
}
node query(LL rt,LL l,LL r){
if(tr[rt].l>=l&&tr[rt].r<=r){
return tr[rt];
}else{
LL mid=(tr[rt].l+tr[rt].r)>>1;
if(r<=mid)return query(rt<<1,l,r);
else if(l>mid)return query(rt<<1|1,l,r);
else{
node t,a=query(rt<<1,l,r),b=query(rt<<1|1,l,r);
t.lson=max(a.lson,a.val+b.lson);
t.rson=max(b.rson,b.val+a.rson);
t.maxn=max(max(a.maxn,b.maxn),a.rson+b.lson);
return t;
}
}
}
int main(){
scanf("%lld%lld",&n,&m);
for(LL i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
build(1,1,n);
while(m--){
LL k;
scanf("%lld",&k);
if(k==1){
LL l,r;
scanf("%lld%lld",&l,&r);
if(l>r)swap(l,r);
printf("%lld\n",query(1,l,r).maxn);
}else if(k==2){
LL p,s;
scanf("%lld%lld",&p,&s);
update(1,p,s);
}
}
}
T4:平衡树
区间翻转,区间修改,区间最大值查询。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define MAXN 100005
#define INF INT_MAX
LL n,m;
struct node{
LL size,pri,val,lazy,rot,ch[2],maxn;
}tr[MAXN*4];
LL rt,root1,root2;
inline void pushup(LL rt){
tr[rt].size=tr[tr[rt].ch[0]].size+tr[tr[rt].ch[1]].size+1;
tr[rt].maxn=tr[rt].val;
if(tr[rt].ch[0])tr[rt].maxn=max(tr[rt].maxn,tr[tr[rt].ch[0]].maxn);
if(tr[rt].ch[1])tr[rt].maxn=max(tr[rt].maxn,tr[tr[rt].ch[1]].maxn);
}
inline void pushdown(LL rt){
if(tr[rt].rot){
if(tr[rt].ch[0])tr[tr[rt].ch[0]].rot^=1;
if(tr[rt].ch[1])tr[tr[rt].ch[1]].rot^=1;
swap(tr[rt].ch[0],tr[rt].ch[1]);
tr[rt].rot=0;
}
if(tr[rt].lazy){
if(tr[rt].ch[0]){
tr[tr[rt].ch[0]].lazy+=tr[rt].lazy;
tr[tr[rt].ch[0]].val+=tr[rt].lazy;
tr[tr[rt].ch[0]].maxn+=tr[rt].lazy;
}
if(tr[rt].ch[1]){
tr[tr[rt].ch[1]].lazy+=tr[rt].lazy;
tr[tr[rt].ch[1]].val+=tr[rt].lazy;
tr[tr[rt].ch[1]].maxn+=tr[rt].lazy;
}
tr[rt].lazy=0;
pushup(rt);
}
}
LL merge(LL x,LL y){
LL now;
pushdown(x);pushdown(y);
if(!x||!y)return x+y;
if(tr[x].pri<tr[y].pri){
now=x;tr[x].ch[1]=merge(tr[x].ch[1],y);
}else{
now=y;tr[y].ch[0]=merge(x,tr[y].ch[0]);
}
pushup(now);return now;
}
void split(LL rt,LL &x,LL &y,LL val){
if(!rt){x=y=0;return ;}
pushdown(rt);
if(tr[tr[rt].ch[0]].size>=val)y=rt,split(tr[rt].ch[0],x,tr[y].ch[0],val);
else x=rt,split(tr[rt].ch[1],tr[x].ch[1],y,val-tr[tr[rt].ch[0]].size-1);
pushup(rt);
}
LL tot;
inline void insert(LL val){
tr[++tot].pri=rand();
tr[tot].val=val;
tr[tot].maxn=val;
tr[tot].size=1;
rt=merge(rt,tot);
}
inline void update1(LL l,LL r,LL val){
LL x,y,z;
split(rt,x,y,l-1);
split(y,y,z,r-l+1);
tr[y].maxn+=val;
tr[y].val+=val;
tr[y].lazy+=val;
rt=merge(merge(x,y),z);
}
inline void update2(LL l,LL r){
LL x,y,z;
split(rt,x,y,l-1);
split(y,y,z,r-l+1);
tr[y].rot^=1;
rt=merge(merge(x,y),z);
}
inline void query(LL l,LL r){
LL x,y,z;
split(rt,x,y,l-1);
split(y,y,z,r-l+1);
printf("%lld\n",tr[y].maxn);
rt=merge(merge(x,y),z);
}
int main(){
srand(time(0));
scanf("%lld%lld",&n,&m);
for(LL i=1;i<=n;i++){
insert(0);
}
while(m--){
LL opt;
scanf("%lld",&opt);
if(opt==1){
LL l,r,x;
scanf("%lld%lld%lld",&l,&r,&x);
update1(l,r,x);
}else if(opt==2){
LL l,r;
scanf("%lld%lld",&l,&r);
update2(l,r);
}else if(opt==3){
LL l,r;
scanf("%lld%lld",&l,&r);
query(l,r);
}
}
}
T5:莫比乌斯反演、递推
虽然是莫反题,但是递推是可以搞的。
先对 l l l和 h h h作如下处理
1.如果 l l l为 k k k的倍数, l l l变为 l k \dfrac{l}{k} kl;如果不是,则变为 ⌊ l k ⌋ + 1 \left\lfloor\dfrac{l}{k}\right\rfloor+1 ⌊kl⌋+1.
2.将 h h h变为 ⌊ h k ⌋ \left\lfloor\dfrac{h}{k}\right\rfloor ⌊kh⌋.
这样,我们就把问题转化为了求在现在的 l l l和 h h h之间选 n n n个数,使得最大公约数刚好为 1 1 1的方案数。
设 f [ i ] f[i] f[i]为最大公约数为 i i i时的方案数(此时选出的 n n n个数不全相同), s s s为区间内 i i i的倍数个数,容易得到 f [ i ] = s n − s f[i]=s^n-s f[i]=sn−s.
但这之中还有些最大公约数不为 i i i的情况没减掉,且这些情况的最大公约数为 2 i 2i 2i、 3 i 3i 3i、 4 i 4i 4i……,我们就可以减去这些情况,得到最终的结果。
此处注意, l = 1 l=1 l=1的时候可以所有的数都选 1 1 1,所以答案要加一。
答案即为 f [ 1 ] − f [ 2 ] − f [ 3 ] f[1]-f[2]-f[3] f[1]−f[2]−f[3]……直到把区间内所有情况减完。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod=1000000007;
LL f[100005];
LL ksm(LL x,LL y){
LL res=1;
while(y){
if(y&1){
res=res*x%mod;
}
y>>=1;x=x*x%mod;
}
return res;
}
int main(){
LL n,k,l,h;
cin>>n>>k>>l>>h;
if(l%k==0){
l=l/k;
}else{
l=l/k+1;
}
h=h/k;
LL s=h-l;
for(LL i=1;i<=s;i++){
LL L=l,R=h;
if(L%i==0)L=L/i;
else L=L/i+1;
R=R/i;
if(L>R)continue;
LL x=R-L+1;
f[i]=ksm(x,n)-x;
f[i]=(f[i]+mod)%mod;
}
LL ans=0;
for(LL i=s;i;i--){
for(LL j=i*2;j<=s;j+=i){
f[i]=(f[i]-f[j]+mod)%mod;
}
}
ans=f[1];
if(l==1)ans++;
printf("%lld",ans);
}