补题 DAY1

P2146 [NOI2015] 软件包管理器

给你一棵树,两个操作:

  • install x 查询路径 \(0\to x\) 上的点权和,并将路径点权赋值为 \(1\)
  • unistall x 查询 \(x\) 的子树点权和,并将子树点权赋值为 \(0\)

树剖板子。恶心。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+3;
int a[maxn],n,m;
    vector<int>e[maxn];
	void add_edge(int u,int v,bool type=1){
		e[u].push_back(v);
		if(type) e[v].push_back(u);//无向图
	}
    #define ls now<<1
    #define rs now<<1|1
    struct T{
        int sum,tag;
    }tree[maxn<<2];
    void pushup(int now){
        tree[now].sum=(tree[ls].sum+tree[rs].sum);
    }
    void pushdown(int now,int l,int r){
        if(tree[now].tag==-1)return;
        int mid=(l+r)>>1;
    	tree[ls].sum=tree[now].tag*(mid-l+1);
    	tree[rs].sum=tree[now].tag*(r-mid);
		tree[ls].tag=tree[rs].tag=tree[now].tag;    
        tree[now].tag=-1;
    }
    void build(int now,int l,int r){
    	tree[now].tag=-1;
        if(l==r){tree[now].sum=1;return;}
        int mid=(l+r)>>1;
        build(ls,l,mid);build(rs,mid+1,r);
        pushup(now);
    }
    void modify(int now,int l,int r,int x,int y,int v){
        if(x<=l&&r<=y){
        	tree[now].sum=v*(r-l+1);
        	tree[now].tag=v;
            return;
        }
        pushdown(now,l,r);
        int mid=(l+r)>>1;
        if(x<=mid)modify(ls,l,mid,x,y,v);
        if(mid+1<=y)modify(rs,mid+1,r,x,y,v);
        pushup(now);
    }
	int query(int now,int l,int r,int x,int y){
        int ans=0;
        if(x<=l&&r<=y)return tree[now].sum;
        pushdown(now,l,r);
        int mid=(l+r)>>1;
        if(x<=mid)ans=(ans+query(ls,l,mid,x,y));
        if(mid+1<=y)ans=(ans+query(rs,mid+1,r,x,y));
        return ans;
    }
    int siz[maxn],dep[maxn],son[maxn],id[maxn],top[maxn],f[maxn],cnt;
    void dfs1(int u,int fa,int depth){
        siz[u]=1; f[u]=fa; dep[u]=depth;
        int maxx=-maxn;
        for(auto v:e[u]){
            if(v!=fa){
                dfs1(v,u,depth+1);
                siz[u]+=siz[v];
                if(siz[v]>maxx) maxx=siz[v],son[u]=v;
            }
        }
    }
    void dfs2(int u,int t){
        id[u]=++cnt; top[u]=t;
        if(!son[u]) return;
		dfs2(son[u],t);
        for(int v:e[u]) if(!id[v]) dfs2(v,v);
    }
    void modify_tree(int x,int y,int z){
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]])swap(x,y);
            modify(1,1,n,id[top[x]],id[x],z);
            x=f[top[x]];
        }
        if(id[x]>id[y])swap(x,y);
        modify(1,1,n,id[x],id[y],z);
    }
    int query_tree(int x,int y){
        int ans=0;
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]])swap(x,y);
            ans=(ans+query(1,1,n,id[top[x]],id[x]));
            x=f[top[x]];
        }
        if(id[x]>id[y])swap(x,y);
        ans=(ans+query(1,1,n,id[x],id[y]));
        return ans;
    }

signed main(){
    cin>>n;
    for(int i=1,u;i<n;i++){
        cin>>u;u++;
        add_edge(i+1,u);
    }
	dfs1(1,1,1); dfs2(1,1);
	build(1,1,n);
	cin>>m;
	string op;
    for(int i=1,x,Ans;i<=m;i++){
        cin>>op>>x;x++;
        if(op=="install") cout<<query_tree(x,1)<<'\n',modify_tree(1,x,0);
        else cout<<siz[x]-query(1,1,n,id[x],id[x]+siz[x]-1)<<'\n', modify(1,1,n,id[x],id[x]+siz[x]-1,1);
    }
}

P3313 [SDOI2014] 旅行

S 国有 \(N\) 个城市,编号从 \(1\)\(N\)。城市间用 \(N-1\) 条双向道路连接,满足从一个城市出发可以到达其它所有城市。每个城市信仰不同的宗教,如飞天面条神教、隐形独角兽教、绝地教都是常见的信仰。

为了方便,我们用不同的正整数代表各种宗教,S 国的居民常常旅行。旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿。当然旅程的终点也是信仰与他相同的城市。S 国为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值。

在 S 国的历史上常会发生以下几种事件:

  • CC x c:城市 \(x\) 的居民全体改信了 \(c\) 教;
  • CW x w:城市 \(x\) 的评级调整为 \(w\)
  • QS x y:一位旅行者从城市 \(x\) 出发,到城市 \(y\),并记下了途中留宿过的城市的评级总和;
  • QM x y:一位旅行者从城市 \(x\) 出发,到城市 \(y\),并记下了途中留宿过的城市的评级最大值。

由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。 为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。

对于 \(100\%\) 的数据,\(N,Q \leq10^5,C \leq10^5\)
数据保证对所有 QSQM 事件,起点和终点城市的信仰相同;在任意时刻,城市的评级总是不大于 \(10^4\) 的正整数,且宗教值不大于 \(C\)

对于每个 \(C\) 建一棵线段树维护区间和,区间 \(\max\),单点修改,动态开点,然后树剖求链和,没了,时间复杂度 \(O(C+n\log^2 n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+3;
int a[maxn],n,m,w[maxn],c[maxn];
int siz[maxn],dep[maxn],son[maxn],id[maxn],top[maxn],f[maxn],cnt;
vector<int>e[maxn];
void add_edge(int u,int v,bool type=1){
	e[u].push_back(v);
	if(type) e[v].push_back(u);
}
struct T{
    int sum,ls,rs,mx;
}tree[maxn<<5];
int tim,root[maxn],node=100000;
void pushup(int pos){
	tree[pos].sum=tree[tree[pos].ls].sum+tree[tree[pos].rs].sum;
	tree[pos].mx=max(tree[tree[pos].ls].mx,tree[tree[pos].rs].mx);
}
int modify(int pos,int l,int r,int k,int val){
	if(!pos) pos=++cnt;
	if(l==r){
		tree[pos].sum=tree[pos].mx=val;
		return pos;
	}
	int mid=(l+r)>>1;
	if(k<=mid) tree[pos].ls=modify(tree[pos].ls,l,mid,k,val);
	else tree[pos].rs=modify(tree[pos].rs,mid+1,r,k,val);
	pushup(pos);
	return pos;
}
int querysum(int pos,int l,int r,int L,int R){
	if(!pos) return 0;
	if(L<=l&&r<=R) return tree[pos].sum;
	int mid=(l+r)>>1,sum=0;
	if(L<=mid) sum+=querysum(tree[pos].ls,l,mid,L,R);
	if(mid+1<=R) sum+=querysum(tree[pos].rs,mid+1,r,L,R);
	return sum;
}
int querymax(int pos,int l,int r,int L,int R){
	if(!pos) return 0;
	if(L<=l&&r<=R) return tree[pos].mx;
	int mid=(l+r)>>1,sum=0;
	if(L<=mid) sum=max(sum,querymax(tree[pos].ls,l,mid,L,R));
	if(mid+1<=R) sum=max(sum,querymax(tree[pos].rs,mid+1,r,L,R));
	return sum;
}
void dfs1(int u,int fa,int depth){
    siz[u]=1; f[u]=fa; dep[u]=depth;
    int maxx=-maxn;
    for(auto v:e[u]){
        if(v!=fa){
            dfs1(v,u,depth+1);
            siz[u]+=siz[v];
            if(siz[v]>maxx) maxx=siz[v],son[u]=v;
        }
    }
}
void dfs2(int u,int t){
    id[u]=++cnt; top[u]=t;
    if(!son[u]) return;
	dfs2(son[u],t);
    for(int v:e[u])
        if(!id[v])
            dfs2(v,v);
}
int querysuml(int pos,int x,int y){
    int ans=0;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        ans=(ans+querysum(pos,1,n,id[top[x]],id[x]));
        x=f[top[x]];
    }
    if(id[x]>id[y])swap(x,y);
    ans=(ans+querysum(pos,1,n,id[x],id[y]));
    return ans;
}
int querymaxl(int pos,int x,int y){
    int ans=0;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        ans=max(ans,querymax(pos,1,n,id[top[x]],id[x]));
        x=f[top[x]];
    }
    if(id[x]>id[y])swap(x,y);
    ans=max(ans,querymax(pos,1,n,id[x],id[y]));
    return ans;
}
signed main(){
    cin>>n>>m; string op;
    for(int i=1;i<=node;i++) root[i]=i;
    for(int i=1;i<=n;i++) cin>>w[i]>>c[i];
    for(int i=1,u,v;i<n;i++) cin>>u>>v,add_edge(u,v);
    dfs1(1,1,1); dfs2(1,1);
    for(int i=1;i<=n;i++) modify(root[c[i]],1,n,id[i],w[i]);
    for(int i=1,x,y;i<=m;i++){
    	cin>>op>>x>>y;
    	if(op=="CC"){
    		modify(root[c[x]],1,n,id[x],0);
    		modify(root[y],1,n,id[x],w[x]);
			c[x]=y;
		}else if(op=="CW"){
			modify(root[c[x]],1,n,id[x],y);
			w[x]=y;
		}else if(op=="QS"){
			cout<<querysuml(root[c[x]],x,y)<<'\n';
		}else{
			cout<<querymaxl(root[c[x]],x,y)<<'\n';
		}
	}
}

P5838 [USACO19DEC] Milk Visits G 弱化版,无修无 \(\max\)

CF914D Bash and a Tough Math Puzzle

  • 给定长度为 \(n\) 的序列 \(a\)\(m\) 次操作。操作有两种:
    1. 1 l r x:若能在 \(a_l​∼a_r\)​ 中 至多 修改一个数,使得 \(\gcd(a_l​,a_{l+1}​,⋯,a_r​)=x\),输出 YES,否则输出 NO。注意我们不需要进行实际的改动。
    2. 2 i y:将 \(a_i\)​ 修改为 \(y\)
  • \(1≤n≤5×10^5\)\(1≤m≤4×10^5\)\(1≤a_i​,x,y≤10^9\)

转化题意,即求 \([l,r]\) 中是否有超过一个数不是 \(x\) 的倍数,是则为 NO,不是则为 YES
于是用线段树维护区间 \(\gcd\),单点修改即可,注意开快读。
时间复杂度 \(O(n\log^2 n)\)

点击查看代码
#include<bits/stdc++.h>  
using namespace std;  
#define ls (now<<1)  
#define rs (now<<1|1)  
//#define int long long  
const int maxn=5e5+3;  
int tree[maxn<<2],a[maxn];  
inline int gcd(int __m, int __n)  
{  
    while (__n != 0)  
    {  
        int __t = __m % __n;  
        __m = __n;  
        __n = __t;  
    }  
    return __m;  
}  
void pushup(int now){  
    tree[now]=gcd(tree[ls],tree[rs]);  
}  
void build(int now,int l,int r){  
    if(l==r){  
        tree[now]=a[l];  
        return;  
    }  
    int mid=(l+r)>>1;  
    build(ls,l,mid); build(rs,mid+1,r);  
    pushup(now);  
}  
void modify(int now,int l,int r,int pos,int v){  
    if(l==r){  
        tree[now]=v;  
        return;  
    }  
    int mid=(l+r)>>1;  
    if(pos<=mid) modify(ls,l,mid,pos,v);  
    else modify(rs,mid+1,r,pos,v);  
    pushup(now);  
}  
int ans;  
void query(int now,int l,int r,int L,int R,int x){  
    if(ans>1) return;  
    if(l==r&&tree[now]%x!=0){ ans++; return; }  
    if(L<=l&&r<=R)  
        if(tree[now]%x==0) return;  
    int mid=(l+r)>>1;  
    if(L<=mid) query(ls,l,mid,L,R,x);  
    if(mid+1<=R) query(rs,mid+1,r,L,R,x);  
    return;  
}  
int n,m;  
signed main(){  
    ios::sync_with_stdio(0);  
    cin>>n;  
    for(int i=1;i<=n;i++) cin>>a[i];  
    build(1,1,n);  
    cin>>m;  
    for(int i=1,op,x,y,z;i<=m;i++){  
        cin>>op>>x>>y; ans=0;  
        if(op&1){  
            cin>>z;  
            query(1,1,n,x,y,z);  
            if(ans>1) cout<<"NO\n";  
            else cout<<"YES\n";  
        }else modify(1,1,n,x,y);  
    }  
    return 0;  
}

P2486 [SDOI2011] 染色

给定一棵 \(n\) 个节点的无根树,共有 \(m\) 个操作,操作分为两种:

  1. 将节点 \(a\) 到节点 \(b\) 的路径上的所有点(包括 \(a\)\(b\))都染成颜色 \(c\)
  2. 询问节点 \(a\) 到节点 \(b\) 的路径上的颜色段数量。

颜色段的定义是极长的连续相同颜色被认为是一段。例如 112221 由三段组成:112221

对于 \(100\%\) 的数据,\(1 \leq n, m \leq 10^5\)\(1 \leq w_i, c \leq 10^9\)\(1 \leq a, b, u, v \leq n\)\(op\) 一定为 CQ,保证给出的图是一棵树。

线段树维护区间颜色段数,当前左右端点颜色,然后做区间覆盖区间查询即可,最后套个树剖,时间复杂度 \(O(n\log^2 n)\)。细节巨多,难点在合并。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define ls (pos<<1)
#define rs (pos<<1|1)
using namespace std;
const int maxn=1e5+3;
int a[maxn],n,m,c[maxn];
int siz[maxn],dep[maxn],son[maxn],id[maxn],top[maxn],f[maxn],cnt,idx[maxn];
vector<int>e[maxn];
void add_edge(int u,int v,bool type=1){
	e[u].push_back(v);
	if(type) e[v].push_back(u);
}
struct T{
    int sum,lc,rc;
}tree[maxn<<2]; int tag[maxn<<2];
void pushup(int pos){
	tree[pos].sum=tree[ls].sum+tree[rs].sum-(tree[ls].rc==tree[rs].lc);
	tree[pos].lc=tree[ls].lc,tree[pos].rc=tree[rs].rc;
}
void build(int pos,int l,int r){
	if(l==r){
		tree[pos].sum=1;
		tree[pos].lc=tree[pos].rc=c[idx[l]];
		return;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid); build(rs,mid+1,r);
	pushup(pos); 
}
void pushdown(int pos){
	if(!tag[pos]) return;
	tag[ls]=tag[rs]=tag[pos];
	tree[ls].lc=tree[ls].rc=tree[rs].lc=tree[rs].rc=tag[pos];
	tree[ls].sum=tree[rs].sum=1;
	tag[pos]=0;
	return ;
}
void modify(int pos,int l,int r,int L,int R,int val){
	if(L<=l&&r<=R){
		tree[pos].sum=1;
		tree[pos].lc=tree[pos].rc=val;
		tag[pos]=val;
		return;
	}
	pushdown(pos);
	int mid=(l+r)>>1;
	if(L<=mid) modify(ls,l,mid,L,R,val);
	if(mid+1<=R) modify(rs,mid+1,r,L,R,val);
	pushup(pos);
}
T querysum(int pos,int l,int r,int L,int R){
	if(L<=l&&r<=R) return tree[pos];
	int mid=(l+r)>>1;
	T sum,q;
	pushdown(pos);
	if(L<=mid&&mid+1<=R){
		sum=querysum(ls,l,mid,L,R);
		q=querysum(rs,mid+1,r,L,R);
		return {sum.sum+q.sum-(sum.rc==q.lc),sum.lc,q.rc};
	}
	if(L<=mid) return querysum(ls,l,mid,L,R);
	if(mid+1<=R) return querysum(rs,mid+1,r,L,R);
}
void dfs1(int u,int fa,int depth){
    siz[u]=1; f[u]=fa; dep[u]=depth;
    int maxx=-maxn;
    for(auto v:e[u]){
        if(v!=fa){
            dfs1(v,u,depth+1);
            siz[u]+=siz[v];
            if(siz[v]>maxx) maxx=siz[v],son[u]=v;
        }
    }
}
void dfs2(int u,int t){
    id[u]=++cnt; top[u]=t; idx[cnt]=u;
    if(!son[u]) return;
	dfs2(son[u],t);
    for(int v:e[u]) if(!id[v]) dfs2(v,v);
}
void modifyl(int x,int y,int val){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        modify(1,1,n,id[top[x]],id[x],val);
		x=f[top[x]];
    }
    if(id[x]>id[y])swap(x,y);
    modify(1,1,n,id[x],id[y],val);
}
int queryl(int x,int y){
    int ans=0; T p,q,r;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        q=querysum(1,1,n,id[top[x]],id[x]);
        p=querysum(1,1,n,id[f[top[x]]],id[f[top[x]]]);
        r=querysum(1,1,n,id[top[x]],id[top[x]]);
        ans+=q.sum-(p.lc==r.rc);
		x=f[top[x]];
    }
    if(id[x]>id[y]) swap(x,y);
    ans=(ans+querysum(1,1,n,id[x],id[y]).sum);
    return ans;
}
signed main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>c[i];
    for(int i=1,u,v;i<n;i++) cin>>u>>v,add_edge(u,v);
    dfs1(1,1,1); dfs2(1,1); build(1,1,n);
    for(int i=1,x,y,z;i<=m;i++){
    	char op;
		cin>>op>>x>>y;
		if(op=='C') cin>>z, modifyl(x,y,z);
		else cout<<queryl(x,y)<<'\n';
	}
}

P7735 [NOI2021] 轻重边

小 W 有一棵 \(n\) 个结点的树,树上的每一条边可能是轻边或者重边。接下来你需要对树进行 \(m\) 次操作,在所有操作开始前,树上所有边都是轻边。操作有以下两种:

  1. 给定两个点 \(a\)\(b\),首先对于 \(a\)\(b\) 路径上的所有点 \(x\)(包含 \(a\)\(b\)),你要将与 \(x\) 相连的所有边变为轻边。然后再将 \(a\)\(b\) 路径上包含的所有边变为重边。
  2. 给定两个点 \(a\)\(b\),你需要计算当前 \(a\)\(b\) 的路径上一共包含多少条重边。

对于所有测试数据:\(T \le 3\)\(1 \le n, m \le {10}^5\)

有个很难想到的 trick(% Future_Player %):
初始时给每个点分配不同颜色,然后修改时将路径上的颜色再分配额外的颜色,这样判断轻/重边就转化为了判断边的两端点异/同色。

证明:
设一个计数器 \(c=n+1\),最开始将 \(1\sim n\) 染色为其编号,然后对于每次覆盖操作将路径上的点染色为 \(c\),再 \(c\leftarrow c+1\),可以发现,路径上的边根据判定方式全部变成了重边,而与路径上的点相连的点的颜色全 \(<c\),即与路径上的点相连的边全部变成了轻边,所以是正确的。

然后就和 P2486 染色 差不多了,只是线段树维护的是区间内连续段的长度与区间覆盖,然后树剖一下即可,时间复杂度 \(O(Tn\log^2 n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define ls (pos<<1)
#define rs (pos<<1|1)
using namespace std;
const int maxn=1e5+3;
int a[maxn],n,m,c[maxn];
int siz[maxn],dep[maxn],son[maxn],id[maxn],top[maxn],f[maxn],cnt,idx[maxn];
vector<int>e[maxn];
void add_edge(int u,int v,bool type=1){
	e[u].push_back(v);
	if(type) e[v].push_back(u);
}
struct T{
    int sum,lc,rc;
}tree[maxn<<2]; int tag[maxn<<2];
void pushup(int pos){
	tree[pos].sum=tree[ls].sum+tree[rs].sum+(tree[ls].rc==tree[rs].lc);
	tree[pos].lc=tree[ls].lc,tree[pos].rc=tree[rs].rc;
}
void pushdown(int pos,int l,int r){
	if(!tag[pos]) return;
	int mid=(l+r)>>1;
	tag[ls]=tag[rs]=tag[pos];
	tree[ls].lc=tree[ls].rc=tree[rs].lc=tree[rs].rc=tag[pos];
	tree[ls].sum=mid-l; tree[rs].sum=r-mid-1;
	tag[pos]=0;
}
void modify(int pos,int l,int r,int L,int R,int val){
	if(L<=l&&r<=R){
		tree[pos].sum=r-l;
		tree[pos].lc=tree[pos].rc=val;
		tag[pos]=val;
		return;
	}
	pushdown(pos,l,r);
	int mid=(l+r)>>1;
	if(L<=mid) modify(ls,l,mid,L,R,val);
	if(mid+1<=R) modify(rs,mid+1,r,L,R,val);
	pushup(pos);
}
T querysum(int pos,int l,int r,int L,int R){
	if(L<=l&&r<=R) return tree[pos];
	int mid=(l+r)>>1;
	T sum,q;
	pushdown(pos,l,r);
	if(L<=mid&&mid+1<=R){
		sum=querysum(ls,l,mid,L,R);
		q=querysum(rs,mid+1,r,L,R);
		return {sum.sum+q.sum+(sum.rc==q.lc),sum.lc,q.rc};
	}
	else if(L<=mid){
		return querysum(ls,l,mid,L,R);
	}
	else if(mid+1<=R){
		return querysum(rs,mid+1,r,L,R);
	}
}
void dfs1(int u,int fa,int depth){
    siz[u]=1; f[u]=fa; dep[u]=depth;
    int maxx=-maxn;
    for(auto v:e[u]){
        if(v!=fa){
            dfs1(v,u,depth+1);
            siz[u]+=siz[v];
            if(siz[v]>maxx) maxx=siz[v],son[u]=v;
        }
    }
}
void dfs2(int u,int t){
    id[u]=++cnt; top[u]=t; idx[cnt]=u;
    if(!son[u]) return;
	dfs2(son[u],t);
    for(int v:e[u])
        if(!id[v])
            dfs2(v,v);
}
void modifyl(int x,int y,int val){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        modify(1,1,n,id[top[x]],id[x],val);
		x=f[top[x]];
    }
    if(id[x]>id[y])swap(x,y);
    modify(1,1,n,id[x],id[y],val);
}
int queryl(int x,int y){
    int ans=0; T p,q,r;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        q=querysum(1,1,n,id[top[x]],id[x]);
        p=querysum(1,1,n,id[f[top[x]]],id[f[top[x]]]);
        r=querysum(1,1,n,id[top[x]],id[top[x]]);
        ans+=q.sum+(p.lc==r.rc);
		x=f[top[x]];
    }
    if(id[x]>id[y]) swap(x,y);
    ans=(ans+querysum(1,1,n,id[x],id[y]).sum);
    return ans;
}
void mian(){
    cin>>n>>m;
    for(int i=1,u,v;i<n;i++) cin>>u>>v,add_edge(u,v);
    dfs1(1,1,1); dfs2(1,1);
    for(int i=1;i<=n;i++) modify(1,1,n,id[i],id[i],i);
    for(int i=1,op,x,y,z=n;i<=m;i++){
		cin>>op>>x>>y;
		if(op==1)
			modifyl(x,y,++z);
		else
			cout<<queryl(x,y)<<'\n';
	}
}
int  T;
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>T;
	while(T--){
		mian();
		cnt=0;
		  memset(id, 0, sizeof id);
		  memset(f, 0, sizeof f);
		  memset(dep, 0, sizeof dep);
		  memset(siz, 0, sizeof siz);
		  memset(son, 0, sizeof son);
		  memset(idx, 0, sizeof idx);
		for(int i=1;i<=n;i++) e[i].clear();
	}
}

20pts,👈🤣不清空导致的

P2894 [USACO08FEB] Hotel G

第一行输入 \(n,m\)\(n\) 代表有 \(n\) 个房间 \((1\leq n \leq 50,000)\),编号为 \(1 \sim n\),开始都为空房,\(m\) 表示以下有 \(m\) 行操作 \((1\leq m < 50,000)\),以下每行先输入一个数 \(i\) ,表示一种操作:

\(i\)\(1\),表示查询房间,再输入一个数 \(x\),表示在 \(1,2,...,n\) 房间中找到长度为 \(x\) 的连续空房,输出连续 \(x\) 个房间中左端的房间号,尽量让这个房间号最小,若找不到长度为 \(x\) 的连续空房,输出 \(0\)。若找得到,在这 \(x\) 个空房间中住上人。

\(i\)\(2\),表示退房,再输入两个数 \(x,y\) 代表房间号 \(x \sim x+y-1\) 退房,即让房间为空。

你需要对每个输入 \(1\) 输出对应的答案。

线段树维护区间最长空房,区间长度,前缀最长空房,后缀最长空房,然后没了,难点在 pushuppushdown,时间复杂度 \(O(m\log n)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ls (pos<<1)
#define rs (pos<<1|1)
int n,m;
const int maxn=5e4+3;
struct T{
	int sum,len,lemp,remp;
}tree[maxn<<2];
int tag[maxn<<2];
void pushup(int pos){
	tree[pos].len=tree[ls].len+tree[rs].len;
	tree[pos].lemp=tree[ls].lemp+(tree[ls].lemp==tree[ls].len?tree[rs].lemp:0);
	tree[pos].remp=tree[rs].remp+(tree[rs].remp==tree[rs].len?tree[ls].remp:0);
	tree[pos].sum=max(max(tree[ls].sum,tree[rs].sum),tree[ls].remp+tree[rs].lemp);
}
void pushdown(int pos,int l,int r){
	if(!tag[pos]) return;
	tag[ls]=tag[rs]=tag[pos];
	tree[ls].sum=tree[ls].lemp=tree[ls].remp=tree[ls].len*(tag[pos]-1);
	tree[rs].sum=tree[rs].lemp=tree[rs].remp=tree[rs].len*(tag[pos]-1);	
	tag[pos]=0;
}
void build(int pos,int l,int r){
	tree[pos].sum=tree[pos].lemp=tree[pos].remp=tree[pos].len=r-l+1;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(ls,l,mid); build(rs,mid+1,r);
}
void modify(int pos,int l,int r,int L,int R,int val){
	if(L<=l&&r<=R){
		tag[pos]=val;
		tree[pos].sum=tree[pos].lemp=tree[pos].remp=tree[pos].len*(tag[pos]-1);	
		return;
	}
	pushdown(pos,l,r);
	int mid=(l+r)>>1;
	if(L<=mid) modify(ls,l,mid,L,R,val);
	if(mid+1<=R) modify(rs,mid+1,r,L,R,val);
	pushup(pos);
}
int query(int pos,int l,int r,int x){
	if(l==r) return 1;
	pushdown(pos,l,r);
	int mid=(l+r)>>1;
	if(tree[ls].sum>=x) return query(ls,l,mid,x);
	if(tree[ls].remp+tree[rs].lemp>=x) return mid-tree[ls].remp+1;
	return query(rs,mid+1,r,x);
}
signed main(){
	cin>>n>>m;
	build(1,1,n);
	for(int i=1,op,x,y;i<=m;i++){
		cin>>op>>x;
		if(op==1){
			if(tree[1].sum<x){
				cout<<"0\n";
				continue;
			}
			int pos=query(1,1,n,x);
			cout<<pos<<'\n';
			modify(1,1,n,pos,pos+x-1,1);
		}else{
			cin>>y;
			modify(1,1,n,x,x+y-1,2);
		}
	}
	return 0;
}
posted @ 2024-02-17 14:04  view3937  阅读(7)  评论(0编辑  收藏  举报
Title