【做題記錄】2025暑假-數(shù)據(jù)結(jié)構(gòu)專題
A. Closest Equals
首先給每個位置求出它到上一次出現(xiàn)的距離,將這記為一條線段。我們發(fā)現(xiàn),有效的線段,其隨著左端點的增長,右端點必然是增長的。因為如果有一條大線段包含了小線段,大線段必然是沒用的。于是我們便可以二分出查詢的線段區(qū)間,ST 表求出最小值即可。
Code
#include<bits/stdc++.h>
#define il inline
#define lwrb lower_bound
#define uprb upper_bound
using namespace std;
namespace asbt{
const int maxn=5e5+5;
int n,m,cnt,ll[maxn],rr[maxn],Log[maxn],st[maxn][22];
map<int,int> pos;
il int query(int l,int r){
if(l>r){
return -1;
}
int p=Log[r-l+1];
return min(st[l][p],st[r-(1<<p)+1][p]);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1,j,x;i<=n;i++){
cin>>x;
j=pos[x];
if(j>ll[cnt]){
ll[++cnt]=j,rr[cnt]=i;
}
pos[x]=i;
}
for(int i=2;i<=cnt;i++){
Log[i]=Log[i>>1]+1;
}
for(int i=1;i<=cnt;i++){
st[i][0]=rr[i]-ll[i];
}
for(int j=1;j<=Log[cnt];j++){
for(int i=1;i+(1<<j)-1<=cnt;i++){
st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
while(m--){
int l,r;
cin>>l>>r;
cout<<query(lwrb(ll+1,ll+cnt+1,l)-ll,uprb(rr+1,rr+cnt+1,r)-rr-1)<<"\n";
}
return 0;
}
}
int main(){return asbt::main();}
B. Fibonacci-ish II
考慮莫隊,用權(quán)值線段樹維護矩陣。當(dāng)新加入一個數(shù)時,要給它后面的數(shù)全都乘上 \(\begin{bmatrix}0&1\\1&1\end{bmatrix}\);刪除時,要給后面都乘上逆矩陣。時間復(fù)雜度 \(O(n\sqrt{n}\log n)\),比較卡常,注意傳參的時間消耗。卡正解常放暴力過的出題人是屑
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define lwrb lower_bound
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace IO{
const int bufsz=1<<20;
char ibuf[bufsz],*p1=ibuf,*p2=ibuf;
#define getchar() (p1==p2&&(p2=(p1=ibuf)+fread(ibuf,1,bufsz,stdin),p1==p2)?EOF:*p1++)
il int read(){
char ch=getchar();
while(ch<'0'||ch>'9'){
ch=getchar();
}
int x=0;
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x;
}
#undef getchar
}
using IO::read;
const int maxn=3e4+5,bln=173;
int n,m,mod,a[maxn],lsh[maxn],tot,sz[maxn<<2],tag[maxn<<2],cnt[maxn],bel[maxn],ans[maxn];
struct node{
int l,r,id;
il bool operator<(const node &x)const{
return (bel[l]^bel[x.l])?l<x.l:bel[l]&1?r>x.r:r<x.r;
}
}wt[maxn];
struct juz{
int a00,a01,a10,a11;
juz(int a00=0,int a01=0,int a10=0,int a11=0):a00(a00),a01(a01),a10(a10),a11(a11){}
il juz operator+(const juz &x)const{
#define pls(x,y) x+y<mod?x+y:x+y-mod
return juz(pls(a00,x.a00),pls(a01,x.a01),pls(a10,x.a10),pls(a11,x.a11));
#undef pls
}
il juz operator*(const juz &x)const{
int t1=a00*x.a00+a01*x.a10,t2=a00*x.a01+a01*x.a11,t3=a10*x.a00+a11*x.a10,t4=a10*x.a01+a11*x.a11;
#define qum(x) x<mod?x:x%mod
return juz(qum(t1),qum(t2),qum(t3),qum(t4));
#undef qum
}
il bool operator!=(const juz &x)const{
return (a00^x.a00)||(a01^x.a01)||(a10^x.a10)||(a11^x.a11);
}
}A,B,I,SA[maxn],SB[maxn],f[maxn],sum[maxn<<2];
il void pushup(int id){
sum[id]=sum[lid]+sum[rid];
sz[id]=sz[lid]+sz[rid];
}
il void pushtag(int id,int v){
tag[id]+=v;
juz t=v>0?SA[v]:SB[-v];
sum[id]=sum[id]*t;
}
il void pushdown(int id){
if(tag[id]){
pushtag(lid,tag[id]);
pushtag(rid,tag[id]);
tag[id]=0;
}
}
il void mul(int id,int L,int R,int l,int r,int v){
if(L>=l&&R<=r){
pushtag(id,v);
return ;
}
pushdown(id);
int mid=(L+R)>>1;
if(l<=mid){
mul(lid,L,mid,l,r,v);
}
if(r>mid){
mul(rid,mid+1,R,l,r,v);
}
pushup(id);
}
il void upd(int id,int l,int r,int p,int v,int rk){
if(l==r){
sum[id]=f[rk+1]*juz(v,0,0,v),sz[id]^=1;
return ;
}
pushdown(id);
int mid=(l+r)>>1;
if(p<=mid){
upd(lid,l,mid,p,v,rk);
}
else{
upd(rid,mid+1,r,p,v,sz[lid]+rk);
}
pushup(id);
}
il void add(int p){
if(cnt[p]++==0){
upd(1,1,tot,p,lsh[p],0);
if(p<tot){
mul(1,1,tot,p+1,tot,1);
}
}
}
il void del(int p){
if(--cnt[p]==0){
upd(1,1,tot,p,0,0);
if(p<tot){
mul(1,1,tot,p+1,tot,-1);
}
}
}
int main(){
// freopen("B.in","r",stdin);
// freopen("my.out","w",stdout);
n=read(),mod=read();
A=juz(0,1,1,1),B=juz(mod-1,1,1,0),I=juz(1,0,0,1);
SA[0]=SB[0]=I;
for(int i=1;i<=n;i++){
a[i]=read();
lsh[++tot]=a[i];
bel[i]=i/bln;
SA[i]=SA[i-1]*A;
SB[i]=SB[i-1]*B;
}
sort(lsh+1,lsh+tot+1);
tot=unique(lsh+1,lsh+tot+1)-lsh-1;
for(int i=1;i<=n;i++){
a[i]=lwrb(lsh+1,lsh+tot+1,a[i])-lsh;
}
for(int i=1;i<=tot;i++){
lsh[i]%=mod;
}
f[1]=juz(0,1);
for(int i=2;i<=n;i++){
f[i]=f[i-1]*A;
}
m=read();
for(int i=1;i<=m;i++){
wt[i].l=read(),wt[i].r=read(),wt[i].id=i;
}
sort(wt+1,wt+m+1);
int l=1,r=0;
for(int i=1;i<=m;i++){
while(l>wt[i].l){
add(a[--l]);
}
while(r<wt[i].r){
add(a[++r]);
}
while(l<wt[i].l){
del(a[l++]);
}
while(r>wt[i].r){
del(a[r--]);
}
ans[wt[i].id]=sum[1].a01;
}
for(int i=1;i<=m;i++){
printf("%d\n",ans[i]);
}
// cerr<<clock()<<"\n";
return 0;
}
/*
5 1000
1 5 2 3 2
5
4 5
2 4
1 4
1 3
4 4
5
15
24
13
3
*/
C. Sasha and Array
沿用上一道題的思路,仍然用線段樹維護矩陣即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=1e5+5,mod=1e9+7;
int n,m,a[maxn];
struct juz{
int mat[2][2];
juz(){
mat[0][0]=mat[0][1]=mat[1][0]=mat[1][1]=0;
}
il int*operator[](int x){
return mat[x];
}
il juz operator+(juz x)const{
juz res;
for(int i=0;i<=1;i++){
for(int j=0;j<=1;j++){
res[i][j]=(mat[i][j]+x[i][j])%mod;
}
}
return res;
}
il juz operator*(juz x)const{
juz res;
for(int i=0;i<=1;i++){
for(int j=0;j<=1;j++){
for(int k=0;k<=1;k++){
(res[i][j]+=mat[i][k]*1ll*x[k][j]%mod)%=mod;
}
}
}
return res;
}
il bool operator!=(juz x)const{
for(int i=0;i<=1;i++){
for(int j=0;j<=1;j++){
if(mat[i][j]!=x[i][j]){
return 1;
}
}
}
return 0;
}
}A,I,sum[maxn<<2],tag[maxn<<2];
il juz qpow(juz x,int y){
juz res;
res[0][0]=res[1][1]=1;
for(;y;y>>=1,x=x*x){
if(y&1){
res=res*x;
}
}
return res;
}
il void pushup(int id){
sum[id]=sum[lid]+sum[rid];
}
il void pushtag(int id,juz v){
tag[id]=tag[id]*v;
sum[id]=sum[id]*v;
}
il void pushdown(int id){
if(tag[id]!=I){
pushtag(lid,tag[id]);
pushtag(rid,tag[id]);
tag[id]=I;
}
}
il void build(int id,int l,int r){
tag[id]=I;
if(l==r){
sum[id]=qpow(A,a[l]);
return ;
}
int mid=(l+r)>>1;
build(lid,l,mid);
build(rid,mid+1,r);
pushup(id);
}
il void mul(int id,int L,int R,int l,int r,juz v){
if(L>=l&&R<=r){
pushtag(id,v);
return ;
}
pushdown(id);
int mid=(L+R)>>1;
if(l<=mid){
mul(lid,L,mid,l,r,v);
}
if(r>mid){
mul(rid,mid+1,R,l,r,v);
}
pushup(id);
}
il juz query(int id,int L,int R,int l,int r){
if(L>=l&&R<=r){
return sum[id];
}
pushdown(id);
int mid=(L+R)>>1;
if(r<=mid){
return query(lid,L,mid,l,r);
}
if(l>mid){
return query(rid,mid+1,R,l,r);
}
return query(lid,L,mid,l,r)+query(rid,mid+1,R,l,r);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
A[0][1]=A[1][0]=A[1][1]=I[0][0]=I[1][1]=1;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,1,n);
while(m--){
int opt,l,r,x;
cin>>opt>>l>>r;
if(opt==1){
cin>>x;
mul(1,1,n,l,r,qpow(A,x));
}
else{
cout<<query(1,1,n,l,r)[0][1]<<"\n";
}
}
return 0;
}
}
int main(){return asbt::main();}
D. New Year Tree
發(fā)現(xiàn) \(c\) 很小,于是考慮開 long long 狀壓,dfn+ 線段樹即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
#define popcntll __builtin_popcountll
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=4e5+5;
int n,m,cnt,a[maxn],L[maxn],R[maxn],stk[maxn];
ll sum[maxn<<2],tag[maxn<<2];
vector<int> e[maxn];
il void dfs(int u,int fa){
stk[++cnt]=u,L[u]=cnt;
for(int v:e[u]){
if(v==fa){
continue;
}
dfs(v,u);
}
R[u]=cnt;
}
il void pushup(int id){
sum[id]=sum[lid]|sum[rid];
}
il void pushtag(int id,ll v){
sum[id]=tag[id]=v;
}
il void pushdown(int id){
if(tag[id]){
pushtag(lid,tag[id]);
pushtag(rid,tag[id]);
tag[id]=0;
}
}
il void build(int id,int l,int r){
if(l==r){
sum[id]=1ll<<a[stk[l]];
return ;
}
int mid=(l+r)>>1;
build(lid,l,mid);
build(rid,mid+1,r);
pushup(id);
}
il void upd(int id,int L,int R,int l,int r,ll v){
if(L>=l&&R<=r){
pushtag(id,v);
return ;
}
pushdown(id);
int mid=(L+R)>>1;
if(l<=mid){
upd(lid,L,mid,l,r,v);
}
if(r>mid){
upd(rid,mid+1,R,l,r,v);
}
pushup(id);
}
il ll query(int id,int L,int R,int l,int r){
if(L>=l&&R<=r){
return sum[id];
}
pushdown(id);
int mid=(L+R)>>1;
ll res=0;
if(l<=mid){
res|=query(lid,L,mid,l,r);
}
if(r>mid){
res|=query(rid,mid+1,R,l,r);
}
return res;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1,u,v;i<n;i++){
cin>>u>>v;
e[u].pb(v),e[v].pb(u);
}
dfs(1,0),build(1,1,n);
while(m--){
int opt,u,x;
cin>>opt>>u;
if(opt==1){
cin>>x;
upd(1,1,n,L[u],R[u],1ll<<x);
}
else{
cout<<popcntll(query(1,1,n,L[u],R[u]))<<"\n";
}
}
return 0;
}
}
int main(){return asbt::main();}
E. Lucky Array
首先將所有 lucky numbers 都打出來,發(fā)現(xiàn) \(1e4\) 以內(nèi)只有 \(30\) 個??紤]線段樹,在每個下標(biāo)存儲該位置與第一個 \(\ge\) 它的幸運數(shù)之差,維護區(qū)間最小值及個數(shù),于是區(qū)間修改就成區(qū)間減了。當(dāng)有的數(shù)被減成了負數(shù),那就把這個位置暴力更新出來。每個位置最多更新 \(30\) 次,于是時間復(fù)雜度 \(O(30n\log n)\)。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define lwrb lower_bound
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=1e5+5,inf=1e9;
int n,m,cnt,a[maxn],b[maxn],tag[maxn<<2];
struct seg{
int val,cnt;
seg(int val=0,int cnt=0):val(val),cnt(cnt){}
il seg operator+(const seg &x)const{
if(val==x.val){
return seg(val,cnt+x.cnt);
}
return val<x.val?*this:x;
}
}tr[maxn<<2];
il void pushup(int id){
tr[id]=tr[lid]+tr[rid];
}
il void pushtag(int id,int v){
tag[id]+=v,tr[id].val+=v;
}
il void pushdown(int id){
if(tag[id]){
pushtag(lid,tag[id]);
pushtag(rid,tag[id]);
tag[id]=0;
}
}
il void reset(int id,int &x){
int t=*lwrb(b+1,b+cnt+1,x);
tr[id]=seg(t-x,1),x=t;
}
il void build(int id,int l,int r){
if(l==r){
reset(id,a[l]);
return ;
}
int mid=(l+r)>>1;
build(lid,l,mid);
build(rid,mid+1,r);
pushup(id);
}
il void add(int id,int L,int R,int l,int r,int v){
if(L>=l&&R<=r){
pushtag(id,v);
return ;
}
pushdown(id);
int mid=(L+R)>>1;
if(l<=mid){
add(lid,L,mid,l,r,v);
}
if(r>mid){
add(rid,mid+1,R,l,r,v);
}
pushup(id);
}
il void upd(int id,int l,int r){
if(l==r){
a[l]-=tr[id].val;
reset(id,a[l]);
return ;
}
pushdown(id);
int mid=(l+r)>>1;
if(tr[lid].val<0){
upd(lid,l,mid);
}
else{
upd(rid,mid+1,r);
}
pushup(id);
}
il seg query(int id,int L,int R,int l,int r){
if(L>=l&&R<=r){
return tr[id];
}
pushdown(id);
int mid=(L+R)>>1;
if(r<=mid){
return query(lid,L,mid,l,r);
}
if(l>mid){
return query(rid,mid+1,R,l,r);
}
return query(lid,L,mid,l,r)+query(rid,mid+1,R,l,r);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
for(int i=1,x;i<=1e4;i++){
x=i;
while(x){
if(x%10!=4&&x%10!=7){
goto togo;
}
x/=10;
}
b[++cnt]=i;
togo:;
}
b[++cnt]=inf;
// cout<<cnt<<"\n";
// for(int i=1;i<=cnt;i++){
// cout<<b[i]<<"\n";
// }
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,1,n);
while(m--){
string opt;
int l,r,x;
cin>>opt>>l>>r;
if(opt[0]=='a'){
cin>>x;
add(1,1,n,l,r,-x);
while(tr[1].val<0){
upd(1,1,n);
}
}
else{
seg ans=query(1,1,n,l,r);
cout<<(ans.val?0:ans.cnt)<<"\n";
}
}
return 0;
}
}
int main(){return asbt::main();}
F. Escape Through Leaf
令 \(dp_u\) 表示 \(u\) 的答案,于是有方程:\(dp_u=\min_{v\in subtree_u}\{dp_v+b_v\times a_u\}\),顯然的李超線段樹。由于要用子樹中的點更新答案,所以需要線段樹合并。根據(jù)勢能分析時間復(fù)雜度 \(O(n\log n)\)。通過精細實現(xiàn)可以做到空間線性。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e5+5;
const ll inf=1e18;
int n,cnt,rt[maxn],ls[maxn],rs[maxn],tr[maxn];
ll a[maxn],b[maxn],dp[maxn];
vector<int> e[maxn];
struct{
ll k,b;
il ll calc(int x){
return k*x+b;
}
}xd[maxn];
il void insert(int &p,int l,int r,int q){
if(!p){
p=q;
ls[p]=rs[p]=0;
return ;
}
ll lp=xd[tr[p]].calc(l),rp=xd[tr[p]].calc(r);
ll lq=xd[tr[q]].calc(l),rq=xd[tr[q]].calc(r);
if(lp>=lq&&rp>=rq){
swap(tr[p],tr[q]);
swap(lp,lq),swap(rp,rq);
}
if(lp<=lq&&rp<=rq){
return ;
}
int mid=(l+r)>>1;
if(xd[tr[p]].calc(mid)>xd[tr[q]].calc(mid)){
swap(tr[p],tr[q]);
swap(lp,lq),swap(rp,rq);
}
if(lq<lp){
insert(ls[p],l,mid,q);
}
else{
insert(rs[p],mid+1,r,q);
}
}
il int merge(int p,int q,int l,int r){
if(!p||!q){
return p+q;
}
if(l==r){
return xd[tr[p]].calc(l)<xd[tr[q]].calc(l)?p:q;
}
int mid=(l+r)>>1;
ls[p]=merge(ls[p],ls[q],l,mid);
rs[p]=merge(rs[p],rs[q],mid+1,r);
insert(p,l,r,q);
return p;
}
il ll query(int id,int l,int r,int p){
if(!id){
return inf;
}
ll res=xd[tr[id]].calc(p);
if(l==r){
return res;
}
int mid=(l+r)>>1;
return min(res,p<=mid?query(ls[id],l,mid,p):query(rs[id],mid+1,r,p));
}
il void dfs(int u,int fa){
for(int v:e[u]){
if(v==fa){
continue;
}
dfs(v,u);
rt[u]=merge(rt[u],rt[v],-1e5,1e5);
}
if(rt[u]){
// cout<<u<<"\n";
dp[u]=query(rt[u],-1e5,1e5,a[u]);
}
xd[u].k=b[u],xd[u].b=dp[u];
tr[++cnt]=u;
insert(rt[u],-1e5,1e5,cnt);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>b[i];
}
for(int i=1,u,v;i<n;i++){
cin>>u>>v;
e[u].pb(v),e[v].pb(u);
}
dfs(1,0);
for(int i=1;i<=n;i++){
cout<<dp[i]<<" ";
}
return 0;
}
}
int main(){return asbt::main();}
G. T-Shirts
考慮排序后遍歷每件 T 恤衫,對 \(\ge c_i\) 的人進行修改。發(fā)現(xiàn)并不好維護。考慮 FHQ-Treap,將平衡樹分裂為三個部分:\([0,c)\),\([c,2c)\),\([2c,+\infty)\)。顯然第一部分不用動,第二部分修改后會 \(<c\),第三部分修改后仍 \(\ge c\)。于是對第二部分直接暴力修改后插入第一部分,第三部分打上懶標(biāo)記再與第一部分合并即可??紤]時間復(fù)雜度,瓶頸顯然在第二部分上。發(fā)現(xiàn)每次頂多變?yōu)樵瓉淼囊话?,因此每個數(shù)最多暴力修改 \(\log\) 次。于是時間復(fù)雜度 \(O(n\log^2)\)。CF 再次爆炸,因此再次無法貼代碼(
upd:CF 又好了
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=2e5+5;
int n,m,rt,tot,ls[maxn],rs[maxn];
int val[maxn],key[maxn],tag[maxn];
int ans[maxn],tans[maxn];
struct node{
int a,b;
il bool operator<(const node &x)const{
return b>x.b||b==x.b&&a<x.a;
}
}p[maxn];
mt19937 rd(time(0));
il int New(int x){
int p=++tot;
ls[p]=rs[p]=tag[p]=0;
val[p]=x,key[p]=rd();
return p;
}
il void pushdown(int p){
if(tag[p]){
tag[ls[p]]+=tag[p];
tag[rs[p]]+=tag[p];
val[ls[p]]+=tag[p];
val[rs[p]]+=tag[p];
tag[p]=0;
}
if(tans[p]){
tans[ls[p]]+=tans[p];
tans[rs[p]]+=tans[p];
ans[ls[p]]+=tans[p];
ans[rs[p]]+=tans[p];
tans[p]=0;
}
}
il int merge(int p,int q){
if(!p||!q){
return p+q;
}
if(key[p]<key[q]){
pushdown(p);
rs[p]=merge(rs[p],q);
return p;
}
else{
pushdown(q);
ls[q]=merge(p,ls[q]);
return q;
}
}
il void split(int id,int &p,int &q,int x){
if(!id){
p=q=0;
return ;
}
pushdown(id);
if(val[id]<x){
p=id;
split(rs[id],rs[p],q,x);
}
else{
q=id;
split(ls[id],p,ls[q],x);
}
}
il void insert(int &id,int x){
int p,q;
split(id,p,q,val[x]);
id=merge(merge(p,x),q);
}
il void dfs(int id,int v,int &x){
if(!id){
return ;
}
pushdown(id);
dfs(ls[id],v,x),dfs(rs[id],v,x);
ls[id]=0,rs[id]=0,val[id]-=v,ans[id]++;
insert(x,id);
}
il void push(int id){
if(!id){
return ;
}
pushdown(id);
push(ls[id]),push(rs[id]);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>p[i].a>>p[i].b;
}
cin>>m;
for(int i=1,x;i<=m;i++){
cin>>x;
insert(rt,New(x));
}
// puts("666");
sort(p+1,p+n+1);
for(int i=1;i<=n;i++){
// cout<<p[i].a<<" "<<p[i].b<<"\n";
// cout<<i<<"\n";
int x,y,z;
// puts("666");
split(rt,x,y,p[i].a);
// puts("777");
split(y,y,z,p[i].a<<1);
// puts("888");
tag[z]-=p[i].a,val[z]-=p[i].a;
tans[z]++,ans[z]++;
dfs(y,p[i].a,x);
rt=merge(x,z);
// for(int j=1;j<=m;j++){
// cout<<ans[j]<<" ";
// }
// cout<<"\n";
}
// puts("666");
push(rt);
for(int i=1;i<=m;i++){
cout<<ans[i]<<" ";
}
return 0;
}
}
int main(){return asbt::main();}
H. New task
將 \((a_1,a_2,a_3,a_4,a_5)\) 分為 \(3\) 個部分,即 \((a_1,a_2)\),\(a_3\),\((a_4,a_5)\),分別記為 \(a\),\(b\),\(c\)。因為要求 \(a_2=a_3=a_4\),考慮在 \(a_2\) 維護 \(a\),在 \(a_4\) 維護 \(c\),對每個 \(a\) 值開線段樹,維護一段區(qū)間的 \(a\)、\(b\)、\(c\)、\(ab\)、\(bc\)、\(abc\) 的數(shù)量。樹狀數(shù)組預(yù)處理出 \(a\) 和 \(c\) 即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define lwrb lower_bound
using namespace std;
namespace asbt{
const int maxn=1e5+5,mod=1e9+7;
il int pls(int x,int y){
return x+y<mod?x+y:x+y-mod;
}
il int sub(int x,int y){
return x<y?x-y+mod:x-y;
}
int n,m,a[maxn],lsh[maxn],tot,qan[maxn],hou[maxn],rt[maxn];
struct{
int tr[maxn];
il int lowbit(int x){
return x&-x;
}
il void build(){
memset(tr,0,sizeof(tr));
}
il void add(int p,int v){
for(;p<=n;p+=lowbit(p)){
tr[p]+=v;
}
}
il int query(int p){
int res=0;
for(;p;p-=lowbit(p)){
res+=tr[p];
}
return res;
}
}F;
struct seg{
int a,b,c,ab,bc,abc;
seg(int a=0,int b=0,int c=0,int ab=0,int bc=0,int abc=0)
:a(a),b(b),c(c),ab(ab),bc(bc),abc(abc){}
il seg operator+(const seg &x)const{
seg res;
res.a=pls(a,x.a),res.b=pls(b,x.b),res.c=pls(c,x.c);
res.ab=pls(pls(ab,x.ab),a*1ll*x.b%mod);
res.bc=pls(pls(bc,x.bc),b*1ll*x.c%mod);
res.abc=pls(pls(abc,x.abc),pls(ab*1ll*x.c%mod,a*1ll*x.bc%mod));
return res;
}
};
struct{
int tot,ls[maxn*18],rs[maxn*18];
seg tr[maxn*18];
il void pushup(int id){
tr[id]=tr[ls[id]]+tr[rs[id]];
}
il void upd(int &id,int l,int r,int p,seg v){
if(!id){
id=++tot;
}
if(l==r){
tr[id]=v;
return ;
}
int mid=(l+r)>>1;
if(p<=mid){
upd(ls[id],l,mid,p,v);
}
else{
upd(rs[id],mid+1,r,p,v);
}
pushup(id);
}
}S;
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
lsh[++tot]=a[i];
}
sort(lsh+1,lsh+tot+1);
tot=unique(lsh+1,lsh+tot+1)-lsh-1;
for(int i=1;i<=n;i++){
a[i]=lwrb(lsh+1,lsh+tot+1,a[i])-lsh;
qan[i]=F.query(a[i]);
F.add(a[i],1);
}
F.build();
for(int i=n;i;i--){
hou[i]=F.query(a[i]);
F.add(a[i],1);
}
int ans=0;
for(int i=1;i<=n;i++){
S.upd(rt[a[i]],1,n,i,seg(qan[i],1,hou[i]));
}
for(int i=1;i<=tot;i++){
ans=pls(ans,S.tr[rt[i]].abc);
}
// cout<<ans<<"\n";
cin>>m;
while(m--){
int opt,x;
cin>>opt>>x;
ans=sub(ans,S.tr[rt[a[x]]].abc);
S.upd(rt[a[x]],1,n,x,opt==1?seg():seg(qan[x],1,hou[x]));
ans=pls(ans,S.tr[rt[a[x]]].abc);
cout<<ans<<"\n";
}
return 0;
}
}
int main(){return asbt::main();}
I. Blog Post Rating
首先可以證明的是,當(dāng)用戶單調(diào)不降,答案一定是最優(yōu)的。證明考慮臨項交換法,設(shè) \(x>y\),那么由下表可知 \((x,y)\) 肯定不比 \((y,x)\) 優(yōu):

我們還可以發(fā)現(xiàn)的是,隨著 \(a_i\) 從小到大,答案一定是先單調(diào)遞減,再單調(diào)不降。那么我們考慮維護一個按 \(a_i\) 大小排序的線段樹,維護每個位置的 \(a_i\) 與經(jīng)過當(dāng)前位置后的答案之差。我們再用三個 set 維護貢獻為 \(0\),\(1\),\(-1\) 的集合。考慮一個新加入的用戶,對其貢獻分類討論:
- 為 \(0\),則直接將其加入集合即可,對后面的用戶沒有影響。
- 為 \(-1\),首先此時它及其之后的差值都會加一,這可能導(dǎo)致最后一個 \(-1\) 變?yōu)?\(0\)。如果最后一個 \(-1\) 不變,那么第一個 \(0\) 將變成 \(1\)。
- 為 \(1\),它及之后的差值全都減一,那么在它之后可能有一個 \(1\) 變成了 \(0\)。
用線段樹維護區(qū)間最小值及其最小下標(biāo)即可。時間復(fù)雜度線性對數(shù)。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pii pair<int,int>
#define fir first
#define sec second
#define mp make_pair
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=5e5+5;
int n,a[maxn],p[maxn],fp[maxn],tag[maxn<<2];
pii mn[maxn<<2];
set<int> _0,_1,__1;
il void pushup(int id){
mn[id]=min(mn[lid],mn[rid]);
}
il void pushtag(int id,int v){
tag[id]+=v,mn[id].fir+=v;
}
il void pushdown(int id){
if(tag[id]){
pushtag(lid,tag[id]);
pushtag(rid,tag[id]);
tag[id]=0;
}
}
il void build(int id,int l,int r){
if(l==r){
mn[id]=mp(a[p[l]],l);
return ;
}
int mid=(l+r)>>1;
build(lid,l,mid);
build(rid,mid+1,r);
pushup(id);
}
il void add(int id,int L,int R,int l,int r,int v){
if(L>=l&&R<=r){
pushtag(id,v);
return ;
}
pushdown(id);
int mid=(L+R)>>1;
if(l<=mid){
add(lid,L,mid,l,r,v);
}
if(r>mid){
add(rid,mid+1,R,l,r,v);
}
pushup(id);
}
il pii query(int id,int L,int R,int l,int r){
if(L>=l&&R<=r){
return mn[id];
}
pushdown(id);
int mid=(L+R)>>1;
if(r<=mid){
return query(lid,L,mid,l,r);
}
if(l>mid){
return query(rid,mid+1,R,l,r);
}
return min(query(lid,L,mid,l,r),query(rid,mid+1,R,l,r));
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i],p[i]=i;
}
sort(p+1,p+n+1,[](int x,int y){return a[x]<a[y];});
for(int i=1;i<=n;i++){
fp[p[i]]=i;
}
build(1,1,n);
for(int i=1;i<=n;i++){
pii val=query(1,1,n,fp[i],fp[i]);
if(val.fir==0){
_0.insert(fp[i]);
}
else if(val.fir<0){
__1.insert(fp[i]);
add(1,1,n,fp[i],n,1);
int t=*__1.rbegin();
if(a[p[t]]==1-(int)__1.size()){
__1.erase(t),_0.insert(t);
add(1,1,n,t,n,-1);
}
else if(_0.size()){
t=*_0.begin();
_0.erase(t),_1.insert(t);
add(1,1,n,t,n,-1);
}
}
else{
_1.insert(fp[i]);
add(1,1,n,fp[i],n,-1);
val=query(1,1,n,fp[i],n);
if(val.fir<0){
_1.erase(val.sec),_0.insert(val.sec);
add(1,1,n,val.sec,n,1);
}
}
cout<<(int)_1.size()-(int)__1.size()<<"\n";
}
return 0;
}
}
int main(){return asbt::main();}

浙公網(wǎng)安備 33010602011771號