2024.9.9
DATE #:20240909
ITEM #:DOC
WEEK #:MONDAY
DAIL #:捌月初柒
TAGS
< BGM = "沧浪行 南海 沧澜主题" > < theme = oi-contest > < [NULL] > < [空] > < [空] >醉后不知天在水,满船清梦压星河 -- 唐珙《题龙阳县青草湖》
得益于这两天的可持久化数据结构复习,这道题做的并不难
//2024.9.9
//by white_ice
#include <bits/stdc++.h>
//#include"../../../need.cpp"
using namespace std;
#define itn int
constexpr int oo=3000006;
int n,m,typ;
int ans,tot,top;
int nl[oo],nr[oo],le[oo],num[oo],st[oo],rt[oo];
struct sag{int ls,rs,sum;}s[oo*30];
struct nod{int v,nxt;}ed[oo];int head[oo],cnt;
void add(int a,int b){ed[++cnt]=(nod){b,head[a]},head[a]=cnt;}
void insert(int x,int& y,int l,int r,int pos){
if (!y||y==x) y=++tot,s[y].sum=s[x].sum;
s[y].sum++;
if (l==r) return;
int mid=l+r >> 1;
if (pos<=mid)
s[y].rs=(!s[y].rs)?s[x].rs:s[y].rs,insert(s[x].ls,s[y].ls,l,mid,pos);
else s[y].ls=(!s[y].ls)?s[x].ls:s[y].ls,insert(s[x].rs,s[y].rs,mid+1,r,pos);
}
int query(int x,int y,int l,int r,int a,int b){
if (a<=l&&r<=b) return s[y].sum-s[x].sum;
int mid=l+r >> 1;
if (b<=mid) return query(s[x].ls,s[y].ls,l,mid,a,b);
if (a >mid) return query(s[x].rs,s[y].rs,mid+1,r,a,b);
return query(s[x].ls,s[y].ls,l,mid,a,b)+query(s[x].rs,s[y].rs,mid+1,r,a,b);
}
main(void){
//fre();
cin.tie(0)->sync_with_stdio(0);
cin >> n >> m >> typ;
for (itn i=1;i<=n;i++) cin >> num[i];
for (int i=1;i<=n;i++){
while (top&&num[st[top]]<num[i])
nr[st[top--]]=i;
if (top) le[i]=st[top];
st[++top]=i;
}
while (top) nr[st[top--]]=n+1;
for (int i=1;i<=n;i++){
while (top&&num[st[top]]<=num[i]) top--;
if (top) nl[i]=st[top];
st[++top]=i;
if (nl[i]&&nl[i]==le[i])
add(nr[i],nl[i]);
}
for (itn i=1;i<=n;i++){
if (i>1) insert(rt[i-1],rt[i],1,n,i-1);
for (itn j=head[i];j;j=ed[j].nxt)
insert(rt[i-1],rt[i],1,n,ed[j].v);
}
for (int a,b,i=1;i<=m;i++){
cin >> a >> b;
a=(a+ans-1)%n+1,b=(b+ans-1)%n+1;
if (a>b) swap(a,b);
printf("%d\n",ans=query(rt[a-1],rt[b],1,n,a,b)),ans *= typ;
}
exit (0);
}
树链剖分,重要的是对线段树的应用,
如何在支持修改前提下求出区间绝对值和?
我们发现加上一个正整数,每个负数变号只有一次机会,所以我们建立两颗线段树,分别维护正数和负数
每次查找是否可能增加到正数,暴力修改单点即可
//2024.9.9
//by white_ice
#include<bits/stdc++.h>
//#include"../../../need.cpp"
using namespace std;
#define int long long
#define itn long long
constexpr int oo = 100005;
constexpr int inf = 1e16;
int n,m;int num[oo];
struct nod{int v,nxt;}st[oo<<1];int head[oo],cnt;
__inline void add(int u,int v){st[++cnt]=(nod){v,head[u]};head[u]=cnt;}
inline namespace treecut{
int fat[oo],son[oo];
int siz[oo],dis[oo];
void dfs1(int x,int fa){
dis[x] = dis[fat[x]=fa]+(siz[x]=1);
for(int v,i=head[x];~i;i = st[i].nxt)
if((v=st[i].v) != fa){
dfs1(v,x);siz[x] += siz[v];
if(siz[v]>siz[son[x]]) son[x] = v;
}
}
int top[oo],new_num[oo],cnt_dfs;
int dfn[oo];
void dfs2(int x,int topx){
top[x] = topx; dfn[x]=++cnt_dfs;
new_num[cnt_dfs] = num[x];
if(son[x]) dfs2(son[x],topx);
for(int v,i = head[x];~i;i = st[i].nxt)
if((v=st[i].v) != fat[x]&&v != son[x])
dfs2(v,v);
}
}
struct sumtree{
int tree[oo<<2],laz[oo<<2];
int siz[oo<<2];
inline void push_up(int x){tree[x] = tree[x<<1]+tree[x<<1|1];siz[x] = siz[x<<1]+siz[x<<1|1];}
void build(int x,int l,int r){
if(l==r){
tree[x] = new_num[l]>=0?new_num[l]:0;
siz[x] = new_num[l]>=0?1:0;
return void();
}
int mid = (l+r) >> 1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
push_up(x);
}
inline void addtag(int x,int p){laz[x] += p;tree[x] += siz[x]*p;}
inline void push_down(int x){
if(laz[x]){
addtag(x<<1,laz[x]);
addtag(x<<1|1,laz[x]);
laz[x] = 0;
}
}
void updata(int x,int l,int r,int nl,int nr,int val){
if(nl==l&&nr==r){addtag(x,val);return void();}
int mid = (l+r) >> 1;
push_down(x);
if(nr<=mid) updata(x<<1,l,mid,nl,nr,val);
else if(nl>mid) updata(x<<1|1,mid+1,r,nl,nr,val);
else updata(x<<1,l,mid,nl,mid,val),updata(x<<1|1,mid+1,r,mid+1,nr,val);
push_up(x);
}
void modify(int x,int l,int r,int nl,int val){
if(l==r){tree[x] = val,siz[x] = 1;return void();}
int mid = (l+r) >> 1;
push_down(x);
if(nl<=mid) modify(x<<1,l,mid,nl,val);
else modify(x<<1|1,mid+1,r,nl,val);
push_up(x);
}
int query(int x,int l,int r,int nl,int nr){
if(nl==l&&nr==r) return tree[x];
int mid = (l+r) >> 1;
push_down(x);
int out;
if(nr<=mid) out = query(x<<1,l,mid,nl,nr);
else if(nl>mid) out = query(x<<1|1,mid+1,r,nl,nr);
else out = query(x<<1,l,mid,nl,mid)+query(x<<1|1,mid+1,r,mid+1,nr);
push_up(x);
return out;
}
}treesum;
struct maxtree{
struct nods{
int val,id;nods(){}
nods(int v,int x):val(v),id(x){}
inline nods operator +(const nods &b)const{
if(val<b.val) return b;return *this;}
};
nods maxn[oo<<2];
int tree[oo<<2],laz[oo<<2];int siz[oo<<2];
inline void push_up(int x){
tree[x] = tree[x<<1]+tree[x<<1|1];
maxn[x] = maxn[x<<1]+maxn[x<<1|1];
siz[x] = siz[x<<1]+siz[x<<1|1];
}
void build(int x,int l,int r){
if(l==r){
maxn[x] = nods(new_num[l]<0?new_num[l]:-inf,l);
siz[x] = new_num[l]<0?1:0;
tree[x] = new_num[l]<0?new_num[l]:0;return;
}
int mid = (l+r) >> 1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
push_up(x);
}
inline void push_down(int x){
if(laz[x]){
laz[x<<1] += laz[x];tree[x<<1] += siz[x<<1]*laz[x];maxn[x<<1].val += laz[x];
laz[x<<1|1] += laz[x];tree[x<<1|1] += siz[x<<1|1]*laz[x];maxn[x<<1|1].val += laz[x];
laz[x] = 0;
}
}
void updata(int x,int l,int r,int nl,int nr,int val){
if(nl==l&&nr==r){
tree[x] += siz[x]*val;
laz[x] += val;
maxn[x].val += val;
return;
}
int mid = (l+r) >> 1;
push_down(x);
if(nr<=mid) updata(x<<1,l,mid,nl,nr,val);
else if(nl>mid) updata(x<<1|1,mid+1,r,nl,nr,val);
else updata(x<<1,l,mid,nl,mid,val),updata(x<<1|1,mid+1,r,mid+1,nr,val);
push_up(x);
}
void modify(int x,int l,int r,int nl){
if(l==r){
tree[x] = siz[x] = 0,maxn[x].val = -inf;return;
}
int mid = (l+r) >> 1;
push_down(x);
if(nl<=mid) modify(x<<1,l,mid,nl);
else modify(x<<1|1,mid+1,r,nl);
push_up(x);
}
nods querymx(int x,int l,int r,int nl,int nr){
if(nl==l&&nr==r) return maxn[x];
int mid = (l+r) >> 1;
push_down(x);
nods out;
if(nr<=mid) out = querymx(x<<1,l,mid,nl,nr);
else if(nl>mid) out = querymx(x<<1|1,mid+1,r,nl,nr);
else out = querymx(x<<1,l,mid,nl,mid)+querymx(x<<1|1,mid+1,r,mid+1,nr);
push_up(x);
return out;
}
int querysum(int x,int l,int r,int nl,int nr){
if(nl==l&&nr==r) return tree[x];
int mid = (l+r) >> 1;
push_down(x);
int out;
if(nr<=mid) out = querysum(x<<1,l,mid,nl,nr);
else if(nl>mid) out = querysum(x<<1|1,mid+1,r,nl,nr);
else out = querysum(x<<1,l,mid,nl,mid)+querysum(x<<1|1,mid+1,r,mid+1,nr);
push_up(x);
return out;
}
void add(int l,int r,int val){
if(r<l) return;
nods out = querymx(1,1,n,l,r);
if(out.val+val>=0){
modify(1,1,n,out.id);
treesum.modify(1,1,n,out.id,out.val+val);
add(l,out.id-1,val);
add(out.id+1,r,val);
}
else updata(1,1,n,l,r,val);
}
}treemax;
main(void){
//fre();
memset(head,-1,sizeof(head));
cin >> n >> m;
for(int i = 1;i<=n;++i) cin >> num[i];
for(int x,y,i = 1;i<n;++i){
cin >> x >> y;
add(x,y),add(y,x);
}
function<void(int,int,int)>plusit=[&](int u,int v,itn d){
while(top[u]!=top[v]){
if(dis[top[u]]<dis[top[v]]) swap(u,v);
treesum.updata(1,1,n,dfn[top[u]],dfn[u],d);
treemax.add(dfn[top[u]],dfn[u],d);
u = fat[top[u]];
}
if(dis[u]<dis[v]) swap(u,v);
treesum.updata(1,1,n,dfn[v],dfn[u],d);
treemax.add(dfn[v],dfn[u],d);
};
function<int(int,int)>findit=[&](int u,int v){
int out = 0;
while(top[u]!=top[v]){
if(dis[top[u]]<dis[top[v]]) swap(u,v);
out += - treemax.querysum(1,1,n,dfn[top[u]],dfn[u])+treesum.query(1,1,n,dfn[top[u]],dfn[u]);
u = fat[top[u]];
}
if(dis[u]<dis[v]) swap(u,v);
return out - treemax.querysum(1,1,n,dfn[v],dfn[u])+treesum.query(1,1,n,dfn[v],dfn[u]);
};
dfs1(1,0),dfs2(1,1);
treemax.build(1,1,n);
treesum.build(1,1,n);
int op,u,v,d;
while(m--){
cin >> op >> u >> v;
if(op==1) cin >> d,plusit(u,v,d);
else cout << findit(u,v) << '\n';
}
exit (0);
}
注意到询问的区间是连续的,我们可以使用分块维护,两边使用树状数组维护
其中一个重要思路&技巧:记f[i][j]为从根到i中出现在第j块中节点的个数
//2024.9.9
//by white_ice
#include<bits/stdc++.h>
//#include"../../../need.cpp"
using namespace std;
#define itn long long
#define int long long
constexpr int oo = 100005;
int n,m;int val[oo];int f[oo],wei[oo];
struct nod{int v,nxt;}st[oo];int head[oo],cnt;
__inline void add(itn a,int b){st[++cnt]=(nod){b,head[a]};head[a] = cnt;}
itn root;
main(void){
//fre();
cin.tie(0)->sync_with_stdio(0);
cin >> n >> m;
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++) cin >> val[i];
for(itn a,b,i=1;i<=n;i++){
cin >> a >> b;
if (a==0)root = b;
else add(a,b),add(b,a);
}
function<void(int,int)>dfs=[&](itn x,int fa){
wei[x] = val[x];
for (itn i=head[x];~i;i=st[i].nxt){
int v = st[i].v;
if (v==fa) continue;
f[v] = x;
dfs(v,x);
wei[x]+=wei[v];
}
};
dfs(root,0);
int op,l,r;
while (m--){
cin >> op >> l >> r;
if (op == 1){
int p = r-val[l];
while (l!=root){
wei[l] += p;
l = f[l];
}
//wei[root] += p;
}
else {
int out = 0;
for (itn i=l;i<=r;i++)out += wei[i];
cout << out << '\n';
}
}
exit(0);
}
打开了dijkstra的新思路:在求出最短路的同时,会对最短路距离进行排序
我们便可依靠此多次求最短路,使用数据结构,枚举通过的边和途径点的个数
//2024.9.9
//by white_ice
#include<bits/stdc++.h>
//#include"../../../need.cpp"
using namespace std;
#define itn unsigned long long
#define int unsigned long long
constexpr int oo = 100005;
int num[oo],n,m;
int block,blo_len,bloc_in[oo];
int tim,root;
int ls[oo],rs[oo],f[oo][320];
int c[oo],tmp[320],wei[oo];
struct nod{int v,nxt;}st[oo<<1];int head[oo],cnt;
void add(int u,int v){st[++cnt]=(nod){v,head[u]};head[u]=cnt;}
void updata(int x,int k){for(;x<=n;x+=x&-x) c[x]+=k;}
int query(int x){int res=0;for(;x;x-=x&-x) res+=c[x];return res;}
void dfs(int x,int fa){
for (itn i=1;i<=blo_len;i++) f[x][i]=f[fa][i];
f[x][bloc_in[x]]++;ls[x]=++tim; wei[x]=num[x];
for(itn i=head[x];~i;i=st[i].nxt){
int v = st[i].v;
if(v==fa)continue;
dfs(v,x),wei[x]+=wei[v];
}
rs[x]=tim;
}
main(void){
//fre();
cin.tie(0)->sync_with_stdio(0);
cin >> n >> m;
block=sqrt(n);blo_len=(n-1)/block+1;
memset(head,-1,sizeof(head));
for(itn i=1;i<=n;i++){cin >> num[i];bloc_in[i]=(i-1)/block+1;}
for(itn u,v,i=1;i<=n;i++){
cin >> u >> v;
if(u==0) root=v;else add(u,v),add(v,u);
}
dfs(root,0);
for(itn i=1;i<=n;i++) tmp[bloc_in[i]]+=wei[i],updata(ls[i],num[i]);
itn op,l,r;
while(m--){
cin >> op;
if(op==1){
cin >> l >> r;updata(ls[l],r-num[l]);
for(itn i=1;i<=blo_len;i++) tmp[i]+=(r-num[l])*f[l][i];num[l]=r;
}
else{
cin >> l >> r;int out=0;int x=bloc_in[l],y=bloc_in[r];
if(x==y) for(itn i=l;i<=r;i++) out+=query(rs[i])-query(ls[i]-1);
else{
for(itn i=l;i<=x*block;i++) out+=query(rs[i])-query(ls[i]-1);
for(int i=(y-1)*block+1;i<=r;i++) out+=query(rs[i])-query(ls[i]-1);
for(itn i=x+1;i<=y-1;i++) out+=tmp[i];
}
cout << out << '\n';
}
}
cout << flush;exit (0);
}
写了一套初赛题
复习了数据结构部分,树状数组,线段树,主席树,并查集(带修,带权,可合并

浙公网安备 33010602011771号