【做题记录】HZOJ 多校-ds

C. [Ynoi2005] rmscne

直接做不好维护,考虑扫描线。用线段树对每个位置 \(i\) 维护 \(p_i\) 表示 \([i,p_i]\)\([i,r]\) 的最小的合法子区间。维护方式很简单,当加入 \(a_r\) 时,设上一次出现的位置为 \(j\),则 \([j+1,r]\)\(p\) 值应赋为 \(r\)

考虑询问 \([l,r]\),我们要找到最大的 \(c\) 使得 \([l,c]\)\([l,r]\) 的合法子区间,答案即为 \(\min_{i=l}^{c}\{p_i-i\}\)。考虑当加入 \(r\) 时,上一次出现的位置 \(j\) 就没用了,\(j\) 对应的 \(c\) 就应指向 \(j+1\),使用并查集即可。

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 pb push_back
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=2e6+5,inf=1e9;
int n,m,a[maxn],fa[maxn],tr[maxn<<2],tag[maxn<<2],pos[maxn],ans[maxn];
vector<pii> q[maxn];
il int find(int x){
    return x!=fa[x]?fa[x]=find(fa[x]):x;
}
il void pushup(int id){
    tr[id]=min(tr[lid],tr[rid]);
}
il void pushtag(int id,int l,int r,int x){
    tag[id]=x,tr[id]=x-r+1;
}
il void pushdown(int id,int l,int r){
    if(tag[id]){
        int mid=(l+r)>>1;
        pushtag(lid,l,mid,tag[id]);
        pushtag(rid,mid+1,r,tag[id]);
        tag[id]=0;
    }
}
il void upd(int id,int L,int R,int l,int r,int x){
    if(L>=l&&R<=r){
        pushtag(id,L,R,x);
        return ;
    }
    pushdown(id,L,R);
    int mid=(L+R)>>1;
    if(l<=mid){
        upd(lid,L,mid,l,r,x);
    }
    if(r>mid){
        upd(rid,mid+1,R,l,r,x);
    }
    pushup(id);
}
il int query(int id,int L,int R,int l,int r){
    if(L>=l&&R<=r){
        return tr[id];
    }
    pushdown(id,L,R);
    int mid=(L+R)>>1,res=inf;
    if(l<=mid){
        res=min(res,query(lid,L,mid,l,r));
    }
    if(r>mid){
        res=min(res,query(rid,mid+1,R,l,r));
    }
    return res;
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        fa[i]=i;
    }
    cin>>m;
    for(int i=1,l,r;i<=m;i++){
        cin>>l>>r;
        q[r].pb(mp(l,i));
    }
    for(int i=1;i<=n;i++){
        upd(1,1,n,pos[a[i]]+1,i,i);
        fa[find(pos[a[i]])]=find(pos[a[i]]+1);
        pos[a[i]]=i;
        for(pii t:q[i]){
            int j=t.fir,id=t.sec;
            ans[id]=query(1,1,n,j,find(j));
        }
    }
    for(int i=1;i<=m;i++){
        cout<<ans[i]<<'\n';
    }
    return 0;
}
}
int main(){return asbt::main();}

D. [Ynoi2019 模拟赛] Yuno loves sqrt technology II

假设 \(n,m\) 同阶。(实则是嫌麻烦)

这个问题显然可以莫队 + 树状数组做,莫队过程中当右端点右移时给答案加上这个区间中比 \(a_r\) 大的个数,其他类似。然而时间复杂度 \(O(n\sqrt{n}\log n)\),需要进一步优化。


莫队二次离线

顾名思义,虽然莫队已经是离线算法了,但其中的一些区间查询(如查询当前 \([l,r]\) 中有多少个数比 \(a_r\) 大)仍然需要较高复杂度,于是我们将这些查询再离线下来,用扫描线的方式解决。


回到本题,这个查询显然可以差分成 \([1,r]\)\([1,l-1]\)\([1,r]\) 可以直接使用树状数组预处理出来,不成问题;我们就将 \([1,l-1]\) 这一部分离线下来。然后对 \(l\) 进行扫描线,我们将在值域上进行 \(O(n\sqrt{n})\) 次区间查询和 \(O(n)\) 次单点修改,于是再分块即可。注意这样求出来的答案实则是相邻两个询问答案之差,需要再进行前缀和。

然而空间复杂度爆炸。考虑莫队左右端点移动的过程,每个 \(l\) 会对应一段区间的 \(r\),于是只存储这一段的左右端点即可,空间线性。

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define lwrb lower_bound
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e5+5,B=316,maxb=319;
int n,m,a[maxn],lsh[maxn];
int bnm,st[maxb],ed[maxb],bel[maxn];
int pre[2][maxn],ans[maxn];
struct node{
	int l,r,id,typ;
	node(int l=0,int r=0,int id=0,int typ=0):l(l),r(r),id(id),typ(typ){}
	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;
	}
}b[maxn];
vector<node> c[maxn];
struct{
	#define lowbit(x) (x&-x)
	int tr[maxn];
	il void add(int p){
		for(;p<=n;p+=lowbit(p)){
			tr[p]++;
		}
	}
	il int query(int p){
		int res=0;
		for(;p;p-=lowbit(p)){
			res+=tr[p];
		}
		return res;
	}
	#undef lowbit
}F;
struct{
	int tr[maxn],tag[maxb];
	il void add(int l,int r){
		if(l>r){
			return ;
		}
		int pl=bel[l],pr=bel[r];
		if(pl==pr){
			for(int i=l;i<=r;i++){
				tr[i]++;
			}
			return ;
		}
		for(int i=pl+1;i<pr;i++){
			tag[i]++;
		}
		for(int i=l;i<=ed[pl];i++){
			tr[i]++;
		}
		for(int i=st[pr];i<=r;i++){
			tr[i]++;
		}
	}
	il int query(int p){
		return tr[p]+tag[bel[p]];
	}
}BL[2];
il void init(){
	cin>>n>>m;
	int cnt=0;
	for(int i=1;i<=n;i++){
//	cerr<<"666\n";
		cin>>a[i];
		lsh[++cnt]=a[i];
	}
	sort(lsh+1,lsh+cnt+1);
	cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
	for(int i=1;i<=n;i++){
		a[i]=lwrb(lsh+1,lsh+cnt+1,a[i])-lsh;
		F.add(a[i]);
		pre[0][i]=pre[0][i-1]+F.query(a[i]-1);
		pre[1][i]=pre[1][i-1]+F.query(n)-F.query(a[i]);
	}
	for(int i=1;i<=m;i++){
		cin>>b[i].l>>b[i].r;
		b[i].id=i;
	}
	bnm=(n+B-1)/B;
	for(int i=1;i<=bnm;i++){
		st[i]=ed[i-1]+1;
		ed[i]=min(ed[i-1]+B,n);
		for(int j=st[i];j<=ed[i];j++){
			bel[j]=i;
		}
	}
}
il void work(){
	sort(b+1,b+m+1);
	int l=1,r=0;
	for(int i=1;i<=m;i++){
		if(r<b[i].r){
			c[l-1].pb(node(r+1,b[i].r,-b[i].id,1));
			ans[b[i].id]+=pre[1][b[i].r]-pre[1][r];
			r=b[i].r;
		}
		if(l>b[i].l){
			c[r].pb(node(b[i].l,l-1,b[i].id,0));
			ans[b[i].id]-=pre[0][l-1]-pre[0][b[i].l-1];
			l=b[i].l;
		}
		if(r>b[i].r){
			c[l-1].pb(node(b[i].r+1,r,b[i].id,1));
			ans[b[i].id]-=pre[1][r]-pre[1][b[i].r];
			r=b[i].r;
		}
		if(l<b[i].l){
			c[r].pb(node(l,b[i].l-1,-b[i].id,0));
			ans[b[i].id]+=pre[0][b[i].l-1]-pre[0][l-1];
			l=b[i].l;
		}
	}
//	cerr<<"777\n";
}
il void solve(){
	for(int i=1;i<=n;i++){
		BL[0].add(a[i]+1,n),BL[1].add(1,a[i]-1);
		for(node t:c[i]){
			for(int j=t.l;j<=t.r;j++){
				if(t.id>0){
					ans[t.id]+=BL[t.typ].query(a[j]);
				}else{
					ans[-t.id]-=BL[t.typ].query(a[j]);
				}
			}
		}
	}
	for(int i=1;i<=m;i++){
		ans[b[i].id]+=ans[b[i-1].id];
	}
	for(int i=1;i<=m;i++){
		cout<<ans[i]<<'\n';
	}
}
int main(){
//	system("fc P5047_1.out my.out");
//	freopen("P5047_1.in","r",stdin);
//	freopen("my.out","w",stdout);
	ios::sync_with_stdio(0),cin.tie(0);
	init(),work(),solve();
	return 0;
}
}
signed main(){return asbt::main();}
/*
5 3
9 3 9 4 7
1 5
2 4
3 5
*/

E. 魔法

首先考虑没有 \(k\) 的限制怎么做。如果没有 \(3\) 操作,我们可以直接用种类并查集维护,给每个枢纽开 \(5\) 个域即可。而加上三操作,相当于每个关系都有一个有效的时间,线段树分治即可。需要可撤销并查集。

那么再考虑加上 \(k\) 的限制,每次会从之前的某一个点开始,很容易想到操作树。但我们仍然希望用类似序列的方法做,于是将操作树再拍到 \(dfn\) 上即可。对于 \(1,2\) 操作直接给整个子树打上标记,对于三操作将原来的区间再拆成两个区间即可。这样做时间复杂度仍然是 \(O(n\log^2n)\) 的。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
#define uprb upper_bound
#define pii pair<int,int>
#define fir first
#define sec second
#define mp make_pair
#define lid id<<1
#define rid id<<1|1
#define ID(x,d) (n*(d)+(x))
using namespace std;
namespace asbt{
const int maxn=1e5+5,inf=1e9;
int n,m,dfn[maxn],cnt,fa[maxn*5],sz[maxn*5],ans[maxn],Ans;
vector<int> e[maxn],tr[maxn<<2]; 
stack<pii> stk;
struct{
	int opt,u,v;
}q[maxn];
set<pii> st[maxn];
il void dfs1(int u){
	dfn[u]=++cnt,sz[u]+=1;
	for(int v:e[u]){
		dfs1(v),sz[u]+=sz[v];
	}
}
il void dfs2(int u){
//	cout<<u<<'\n';
	switch(q[u].opt){
		case 1:
		case 2:{
			st[u].insert(mp(dfn[u],dfn[u]+sz[u]-1));
			break;
		}case 3:{
			int x=q[u].u;
			auto t=prev(st[x].uprb(mp(dfn[u],inf)));
			if(dfn[u]>t->fir){
				st[x].insert(mp(t->fir,dfn[u]-1));
			}
			if(dfn[u]+sz[u]<=t->sec){
				st[x].insert(mp(dfn[u]+sz[u],t->sec));
			}
			st[x].erase(t);
			break;
		}
	}
	for(int v:e[u]){
		dfs2(v);
	}
}
il int find(int x){
	return x!=fa[x]?find(fa[x]):x;
}
il void merge(int u,int v){
	u=find(u),v=find(v);
	if(u==v){
		return ;
	}
	if(sz[u]>sz[v]){
		swap(u,v);
	}
	fa[u]=v,sz[v]+=sz[u];
	stk.push(mp(u,v));
}
il void chx(){
	int u=stk.top().fir,v=stk.top().sec;
	stk.pop(),fa[u]=u,sz[v]-=sz[u];
}
il bool chk(int x){
	int f[5]={find(ID(x,0)),find(ID(x,1)),find(ID(x,2)),find(ID(x,3)),find(ID(x,4))};
	sort(f,f+5);
	for(int i=0;i<=3;i++){
		if(f[i]==f[i+1]){
			return 1;
		}
	}
	return 0;
}
il void upd(int id,int L,int R,int l,int r,int x){
	if(L>=l&&R<=r){
		tr[id].pb(x);
		return ;
	}
	int mid=(L+R)>>1;
	if(l<=mid){
		upd(lid,L,mid,l,r,x);
	}
	if(r>mid){
		upd(rid,mid+1,R,l,r,x);
	}
}
il void work(int id,int l,int r){
	int top=stk.size();
	for(int x:tr[id]){
		int u=q[x].u,v=q[x].v,t=q[x].opt;
		for(int i=0;i<=4;i++){
			merge(ID(u,i),ID(v,(i+t)%5));
		}
	}
	for(int x:tr[id]){
		if(chk(q[x].u)||chk(q[x].v)){
			Ans++;
		}
	}
	if(l==r){
		ans[l]=Ans;
	}else{
		int mid=(l+r)>>1;
		work(lid,l,mid);
		work(rid,mid+1,r);
	}
	for(int x:tr[id]){
		if(chk(q[x].u)||chk(q[x].v)){
			Ans--;
		}
	}
	while(stk.size()>top){
		chx();
	}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	for(int i=1,x;i<=m;i++){
		cin>>x>>q[i].opt>>q[i].u;
		e[x].pb(i);
		if(q[i].opt<3){
			cin>>q[i].v;
		}
	}
//	puts("666");
	dfs1(0);
//	puts("777");
	dfs2(0);
//	puts("888");
	for(int i=1;i<=m;i++){
//		cout<<i<<'\n';
		for(pii j:st[i]){
			upd(1,1,cnt,j.fir,j.sec,i);
		}
	}
	for(int i=1;i<=n*5;i++){
		fa[i]=i,sz[i]=1;
	}
//	puts("999");
	work(1,1,cnt);
//	puts("000");
	for(int i=1;i<=m;i++){
		cout<<(ans[dfn[i]]?"naive":"excited")<<'\n';
	}
	return 0;
}
}
int main(){return asbt::main();}

F. [JSOI2008] Blue Mary 开公司

每种设计方案对应一条直线,李超线段树即可。

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;
int n;
struct line{
	double k,b;
	line(double k=0,double b=0):k(k),b(b){}
	il double calc(int x){
		return k*x+b;
	}
}tr[maxn<<1];
il void insert(int id,int l,int r,line x){
	int mid=(l+r)>>1;
	if(tr[id].calc(mid)<x.calc(mid)){
		swap(tr[id],x);
	}
	double lo=tr[id].calc(l),ro=tr[id].calc(r);
	double ln=x.calc(l),rn=x.calc(r);
	if(ln>lo){
		insert(lid,l,mid,x);
	}else if(rn>ro){
		insert(rid,mid+1,r,x);
	}
}
il double query(int id,int l,int r,int p){
	if(l==r){
		return tr[id].calc(p);
	}
	int mid=(l+r)>>1;
	if(p<=mid){
		return max(tr[id].calc(p),query(lid,l,mid,p));
	}else{
		return max(tr[id].calc(p),query(rid,mid+1,r,p));
	}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	while(n--){
		string opt;
		cin>>opt;
		if(opt[0]=='P'){
			double s,p;
			cin>>s>>p;
			insert(1,1,5e4,line(p,s-p));
		}else{
			int x;
			cin>>x;
			cout<<(int)(query(1,1,5e4,x)/100)<<'\n';
		}
	}
	return 0;
}
}
int main(){return asbt::main();}

G. [HEOI2016/TJOI2016] 排序

线段树(马思博) O

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;
int n,m,q,a[maxn],tr[maxn<<2][2],tag[maxn<<2];
struct{
	int opt,l,r;
}b[maxn];
il void pushup(int id){
	tr[id][0]=tr[lid][0]+tr[rid][0];
	tr[id][1]=tr[lid][1]+tr[rid][1];
}
il void pushtag(int id,int l,int r,int x){
	tr[id][x]=r-l+1,tr[id][x^1]=0;
	tag[id]=x;
}
il void pushdown(int id,int l,int r){
	if(~tag[id]){
		int mid=(l+r)>>1;
		pushtag(lid,l,mid,tag[id]);
		pushtag(rid,mid+1,r,tag[id]);
		tag[id]=-1;
	}
}
il void build(int id,int l,int r,int x){
	tag[id]=-1;
	if(l==r){
		tr[id][0]=tr[id][1]=0;
		tr[id][a[l]>=x]=1;
		return ;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid,x);
	build(rid,mid+1,r,x);
	pushup(id);
}
il void upd(int id,int L,int R,int l,int r,int x){
	if(l>r){
		return ;
	}
	if(L>=l&&R<=r){
		pushtag(id,L,R,x);
		return ;
	}
	pushdown(id,L,R);
	int mid=(L+R)>>1;
	if(l<=mid){
		upd(lid,L,mid,l,r,x);
	}
	if(r>mid){
		upd(rid,mid+1,R,l,r,x);
	}
	pushup(id);
}
il int query(int id,int L,int R,int l,int r){
	if(L>=l&&R<=r){
		return tr[id][0];
	}
	pushdown(id,L,R);
	int mid=(L+R)>>1,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;
}
il bool check(int x){
	build(1,1,n,x);
	for(int i=1;i<=m;i++){
//		cout<<i<<'\n';
		int l=b[i].l,r=b[i].r;
		int c0=query(1,1,n,l,r),c1=r-l+1-c0;
//		puts("666");
		if(b[i].opt){
			upd(1,1,n,l,l+c1-1,1);
			upd(1,1,n,l+c1,r,0);
		}else{
//			puts("777");
			upd(1,1,n,l,l+c0-1,0);
//			puts("888");
			upd(1,1,n,l+c0,r,1);
//			puts("999");
		}
	}
	return query(1,1,n,q,q);
}
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;i<=m;i++){
		cin>>b[i].opt>>b[i].l>>b[i].r;
	}
	cin>>q;
	int l=1,r=n;
	while(l<r){
		int mid=(l+r+1)>>1;
		if(check(mid)){
			r=mid-1;
		}else{
			l=mid;
		}
	}
	cout<<l;
	return 0;
}
}
int main(){return asbt::main();}
posted @ 2025-11-09 19:57  zhangxy__hp  阅读(7)  评论(0)    收藏  举报