Loading

2024.9.9


DATE #:20240909

ITEM #:DOC

WEEK #:MONDAY

DAIL #:捌月初柒

TAGS

< BGM = "沧浪行 南海 沧澜主题" >
< theme = oi-contest >
< [NULL] >
< [空] > 
< [空] >
醉后不知天在水,满船清梦压星河 -- 唐珙《题龙阳县青草湖》

A. Count

得益于这两天的可持久化数据结构复习,这道题做的并不难

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

B. Abs

树链剖分,重要的是对线段树的应用,

如何在支持修改前提下求出区间绝对值和?

我们发现加上一个正整数,每个负数变号只有一次机会,所以我们建立两颗线段树,分别维护正数和负数

每次查找是否可能增加到正数,暴力修改单点即可

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

C. 普通计算姬

注意到询问的区间是连续的,我们可以使用分块维护,两边使用树状数组维护

其中一个重要思路&技巧:记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);
}

D. Road

打开了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);
}

写了一套初赛题

复习了数据结构部分,树状数组,线段树,主席树,并查集(带修,带权,可合并

posted @ 2024-09-09 22:03  white__ice  阅读(6)  评论(0)    收藏  举报