[CF1558D]Top-Notch Insertions

Top-Notch Insertions

题解

首先,原操作相当于一种排序的方式,每次操作相当于告诉我们这个数比现在已经操作过且在它后面的数小,不小于它前面的数。
我们可以理解成加一个 < < < ⩽ \leqslant 的符号。
如果我们将 i i i插入到 a j − 1 a_{j-1} aj1 a j a_{j} aj之间,相当于将原来两者的关系变为 a j − 1 ⩽ i < a j a_{j-1}\leqslant i<a_{j} aj1i<aj
很容易发现,上面的操作我们可以用平衡树进行维护,注意 ∑ m ⩽ 2 × 1 0 5 \sum m\leqslant 2\times 10^5 m2×105 ∑ n \sum n n不保证范围,所以我们只能将它给出的操作插入到平衡树中。

既然我们可以统计出 < < <的的个数,那么我们也知道至少有多少个不同的数,我们可以枚举相同的连续 ⩽ \leqslant 段与总共的不同的数的个数。
但这样仍然会使得我们的复杂度中存在 O ( ∑ n ) O\left(\sum n\right) O(n)因为这样差不多是从 m m m n n n枚举了,如果我们要让它从 0 0 0 m m m的话就只能容斥了。
但实际上我们有一种更加简便的做法。
如果全都是 ⩽ \leqslant 的话我们的方案数是 ( 2 n n ) \binom{2n}{n} (n2n),相当于就是差分的形式,选择的点前面有多少个点没有选择,就是它的值。
如果加入 < < <我们可以看作每个 < < <自己绑定一个没选的空点,与上面一样,方案数为 ( 2 n − t n ) \binom{2n-t}{n} (n2nt) t t t表示有多少个 < < <

时间复杂度 O ( ∑ m l o g   m ) O\left(\sum mlog\,m\right) O(mlogm)

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define MAXN 400005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define lson (rt<<1)
#define rson (rt<<1|1)
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=0x3f3f3f3f3f3f;
const int mo=998244353;
const int inv2=499122177;
const int jzm=2333;
const int zero=10000;
const int lim=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-7;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,m,p[MAXN];
int ans,fac[MAXN],inv[MAXN],f[MAXN];
void init(){
	fac[0]=fac[1]=inv[0]=inv[1]=f[1]=1;
	for(int i=2;i<=4e5;i++)
		fac[i]=1ll*i*fac[i-1]%mo,
		f[i]=1ll*(mo-mo/i)*f[mo%i]%mo,
		inv[i]=1ll*f[i]*inv[i-1]%mo;
}
int C(int x,int y){
	if(x<0||y<0||x<y)return 0;
	return 1ll*fac[x]*inv[y]%mo*inv[x-y]%mo;
}
struct ming{
	int siz,rnd,val,ch[2],sum,lzy,id;
	ming(){siz=rnd=val=sum=ch[0]=ch[1]=lzy=0;}
};
class FHQ_Treap{
	private:
		ming tr[MAXN];int tot,root;
		int newnode(int x,int y){
			int rt=++tot;tr[rt].siz=1;tr[rt].rnd=rand();
			tr[rt].ch[0]=tr[rt].ch[1]=tr[rt].lzy=0;
			tr[rt].val=tr[rt].sum=x;tr[rt].id=y;return rt; 
		}
		void pushdown(int rt){
			if(tr[rt].lzy){
				if(tr[rt].ch[0])tr[tr[rt].ch[0]].lzy+=tr[rt].lzy,tr[tr[rt].ch[0]].id+=tr[rt].lzy;
				if(tr[rt].ch[1])tr[tr[rt].ch[1]].lzy+=tr[rt].lzy,tr[tr[rt].ch[1]].id+=tr[rt].lzy;
				tr[rt].lzy=0;
			}
		}
		void pushup(int rt){
			tr[rt].siz=1+tr[tr[rt].ch[0]].siz+tr[tr[rt].ch[1]].siz;
			tr[rt].sum=tr[rt].val+tr[tr[rt].ch[0]].sum+tr[tr[rt].ch[1]].sum;
		}
		void split(int now,int k,int &x,int &y){
			if(!now){x=y=0;return ;}pushdown(now);
			if(k==tr[now].id){
				y=tr[now].ch[1],tr[now].ch[1]=0,x=now;
				pushup(x);return ;	
			}
			if(k>=tr[now].id)
				x=now,split(tr[now].ch[1],k,tr[x].ch[1],y),pushup(x);
			else y=now,split(tr[now].ch[0],k,x,tr[y].ch[0]),pushup(y);
		}
		int merge(int a,int b){
			if(!a||!b)return a+b;pushdown(a);pushdown(b);
			if(tr[a].rnd<tr[b].rnd){
				tr[a].ch[1]=merge(tr[a].ch[1],b);
				pushup(a);return a;
			}
			tr[b].ch[0]=merge(a,tr[b].ch[0]);
			pushup(b);return b;
		}
	public:
		void insert(int ip,int k){
			int x1,y1,x2,y2;split(root,k-1,x1,y1);
			split(x1,k-2,x2,y2);if(y1)tr[y1].lzy++,tr[y1].id++;
			if(y2)tr[y2].val=tr[y2].sum=0;
			root=merge(merge(x2,y2),merge(newnode(ip!=k,k),y1));
		}
		int query(){return tr[root].sum;}
		void clear(){
			for(int i=1;i<=tot;i++)
				tr[i].ch[0]=tr[i].ch[1]=tr[i].val=tr[i].rnd=tr[i].siz=tr[i].lzy=tr[i].id=0;
			root=tot=0;
		}
}T; 
signed main(){
	int tt;read(tt);init();
	while(tt--){
		read(n);read(m);ans=0;
		for(int i=1,x,y;i<=m;i++)read(x),read(y),T.insert(x,y);//,T.Search();
		int t=T.query();printf("%d\n",C(n+n-t-1,n));T.clear();
	}
	return 0;
}

谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值