• 【CF1558D】TopNotch Insertions


    题目

    题目链接:https://codeforces.com/problemset/problem/1558/D
    对于一个长度为 \(n\) 的序列 \(a\) 做插入排序,即依次考虑 \(a_2, \cdots, a_n\) 每个元素:

    • 如果 \(a_i \ge a_{i - 1}\),即前缀依然保持不下降,不做操作。
    • 否则,找到最靠前的位置 \(p\),使得 \(a_i< a_p\),然后将 \(a_i\) 插入到 \(a_p\) 前面,并重新标号序列。记这次插入为 \((i, p)\)

    接下来给定 \(m\) 个二元组 \((x_i, y_i)\),求有多少个长度为 \(n\) 的序列 \(a\),满足 \(\forall i, a_i \in [1, n] \cap \mathbb N\) 且做插入排序恰好进行给定的 \(m\) 次插入。
    输出序列的数量对 \(998244353\) 取模的结果。多测。

    \(Q\leq 10^5\)\(n,\sum m\leq 2\times 10^5\)

    思路

    手玩一下发现,最后肯定是一个长度为 \(n\) 的序列,相邻两项之间有一个 \(\leq\)\(<\) 的限制,求值域在 \([1,n]\) 范围内的序列数量。
    这个东西只需要求出有多少个 \(<\),然后组合数随便计算一下就好了。
    时间倒流,设处理到操作 \((x_i,y_i)\),那么现在的 \(n\) 个空位中就有若干个已经填上了 \((x_i,n]\) 之间的数。那么这一个操作说明 \(x_i\) 一定是填在目前的第 \(y_i\) 个空位中,且严格小于第 \(y_i+1\) 个空位上的数。
    那么这一次操作会把 \(<\) 的数量加一,当且仅当第 \(y_i\) 个空位与第 \(y_i+1\) 个空位是相邻的。
    所以可以用线段树维护区间空位数量,每次操作在线段树上二分即可。
    但是这个题还有一个比较无聊的地方,就是没有保证 \(\sum n\) 的范围。所以每一组询问都重建线段树是不可行的。只需要每组询问搞定之后把操作撤回就行了。
    时间复杂度 \(O(m\log n)\)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=200010,MOD=998244353;
    int Q,n,m,cnt,a[N];
    ll fac[N*2];
    queue<int> q;
    
    ll fpow(ll x,ll k)
    {
    	ll ans=1;
    	for (;k;k>>=1,x=x*x%MOD)
    		if (k&1) ans=ans*x%MOD;
    	return ans;
    }
    
    void init()
    {
    	fac[0]=1;
    	for (int i=1;i<2*N;i++)
    		fac[i]=fac[i-1]*i%MOD;
    }
    
    ll C(int n,int m)
    {
    	if (n<m) return 0;
    	return fac[n]*fpow(fac[m],MOD-2)%MOD*fpow(fac[n-m],MOD-2)%MOD;
    }
    
    struct SegTree
    {
    	int cnt[N*4];
    	
    	void build(int x,int l,int r)
    	{
    		cnt[x]=r-l+1;
    		if (l==r) return;
    		int mid=(l+r)>>1;
    		build(x*2,l,mid); build(x*2+1,mid+1,r);
    	}
    	
    	void update(int x,int l,int r,int k,int v)
    	{
    		cnt[x]+=v;
    		if (l==r) return;
    		int mid=(l+r)>>1;
    		if (k<=mid) update(x*2,l,mid,k,v);
    		if (k>mid) update(x*2+1,mid+1,r,k,v); 
    	}
    	
    	int query(int x,int l,int r,int k)
    	{
    		if (l==r) return l;
    		int mid=(l+r)>>1;
    		if (cnt[x*2]>=k) return query(x*2,l,mid,k);
    			else return query(x*2+1,mid+1,r,k-cnt[x*2]);
    	}
    }seg;
    
    int main()
    {
    	init();
    	seg.build(1,1,N-1);
    	scanf("%d",&Q);
    	while (Q--)
    	{
    		scanf("%d%d",&n,&m);
    		for (int i=1,x;i<=m;i++)
    			scanf("%d%d",&x,&a[i]);
    		cnt=0;
    		for (int i=m;i>=1;i--)
    		{
    			int p1=seg.query(1,1,N-1,a[i]+1),p2=seg.query(1,1,N-1,a[i]);
    			if (p1-p2==1) cnt++;
    			seg.update(1,1,N-1,p2,-1);
    			q.push(p2);
    		}
    		cout<<C(n*2-cnt-1,n)<<"\n";
    		for (;q.size();q.pop())
    			seg.update(1,1,N-1,q.front(),1);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Oracle修改表Table所属表空间及Clob、Blob字段的处理
    MyBatis返回多表连接结果
    MyBatis查询结果resultType返回值类型详细介绍
    SpringBoot之分页PageHelper
    Postman简单用法以及转cURL等命令的正确姿势
    postman 巧用cURL
    Spring Boot设置跨域访问
    springboot设置cors跨域请求的两种方式
    @Configuration使用
    @GetMapping和@PostMapping接收参数的格式
  • 原文地址:https://www.cnblogs.com/stoorz/p/15416594.html
Copyright © 2020-2023  润新知