洛谷 P3960 列队 线段树动态开点

题意:n*m个人,每次从x,y出去一个,第x行统一向左补位,第m列统一向前移位,每次输出当前位置人的编号
思路:
模拟过程很容易想到用线段树或者BIT维护+vector维护,但是不太行,于是学了动态开点,50行不压行代码。
代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0)
const int maxn = 3e5+5;
int n,m,q,sz,L = 1,R = 6e5+5,root[maxn];
vector<ll>a[maxn];
class dsegment_tree{public:
	struct dsegment_node{
		int son[2],sum;
	}node[maxn*50];
	int query(int k,int now,int l = L,int r = R){
		if(l==r) return l;
		int mid = l+r>>1,res = 0,cnt = mid-l+1-node[node[now].son[0]].sum;
		if(cnt>=k) res = query(k,node[now].son[0],l,mid);
		else res = query(k-cnt,node[now].son[1],mid+1,r);
		return res;
	}
	void update(int pos,int &now,int l = L,int r = R){
		if(!now) now = ++sz;node[now].sum++;
		if(l==r) return;
		int mid = l+r>>1;
		if(pos<=mid) update(pos,node[now].son[0],l,mid);
		else update(pos,node[now].son[1],mid+1,r);
	}
}tree;
ll dely(int x,bool flag){
	int pos = tree.query(x,root[0]);
	tree.update(pos,root[0]);
	ll ans = 0;
	if(pos<=n) ans = 1ll*pos*m;
	else ans = a[0][pos-n-1];
	if(flag) a[0].push_back(ans);
	return ans; 
}
ll delx(int x,int y){
	int pos = tree.query(y,root[x]);
	tree.update(pos,root[x]);
	ll ans = 0;
	if(pos<m) ans = 1ll*(x-1)*m+pos;
	else ans = a[x][pos-m];
	a[x].push_back(dely(x,0));
	a[0].push_back(ans);
	return ans;
}
int main(){
	IO;cin>>n>>m>>q;
	forn(i,q){
		int x,y;cin>>x>>y;
		if(y!=m) cout<<delx(x,y)<<'
';
		else cout<<dely(x,1)<<'
';
	}
	return 0;
}
原文地址:https://www.cnblogs.com/AlexPanda/p/12520282.html