题解 LGP6852【Mex】/ SS230329A【签到题】

题目链接:http://cplusoj.com/d/master/p/33

problem

一个长为 \(n\) 排列(下标和值从零开始),要满足 \(m\) 个限制:\(mex(a_l,a_{l+1},\cdots,a_r)=x\),求排列方案数和最小字典序解。\(n\leq 10^5\)

solution

\(mex(a_l,a_{l+1},\cdots,a_r)=x\) 相当于 \(0,1,2,\cdots,x-1\) 都要有,然后 \(x\) 不能有。

考虑预处理 \(can_i\) 表示 \(i\) 能填的区间(区间的交还是区间),如果是空集就寄。
然后在看一下 \(x=i\) 的区间的并记为 \(no_i\),两种情况:

  • \(i=0\) 此时这些不能填的位置不连续
  • \(i>0\) 此时不能填的位置一定连续(如果不连续,\(i-1\) 能填的就是空集)

我们想一下 \(i\) 能放在哪里:

  • 如果 \(no_i=\varnothing\),因为 \(0,1,2,\cdots,i-1\) 的范围都是 \(can_i\) 的子集,所以能放的位置是:\(|can_i|-i\)
  • 如果 \(i=0\) 则能放的只有 \(|can_i|-|no_i|\)
  • 如果 \(i>0\),因为 \(can_{i-1}\subseteq no_i\)(并大于交),所以认为 \(0,1,2,\cdots,i-1\) 都在 \(no_i\) 里,所以能放的是 \(|can_i|-|no_i|\)

所以方案数就数完了,至于方案可以用并查集维护一下能放的位置,根据刚才的讨论从小到大填数。

code

点击查看代码
#include <tuple>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
const int P=998244353;
void red(LL&x){x%=P;}
struct Impossible{};
struct rang{
	int l,r;
	rang(int l=1,int r=0):l(l),r(r){}
	bool empty(){return l>r;}
	int len(){return r-l+1;}
	rang operator^(rang b){return rang(max(l,b.l),min(r,b.r));} 
	rang operator+(rang b){return rang(min(l,b.l),max(r,b.r));}
	bool operator!=(rang b){return l!=b.l||r!=b.r;}
};
int n,m;
vector<rang> q[1<<17];
namespace BF{
//	void bruteforce(){
//		int cnt=0;
//		vector<int> per(n),ans;
//		auto mex=[&](int l,int r){
//			vector<bool> vis(n,0);
//			for(int i=l;i<=r;i++) vis[per[i]]=1;
//			for(int i=0;i<n;i++) if(!vis[i]) return i;
//			return n;	
//		};
//		for(int i=0;i<n;i++) per[i]=i;
//		do{
//			bool flag=1;
//			for(auto&a:q){
//				int l,r,x; tie(l,r,x)=a;
//				if(mex(l,r)!=x){flag=0;break;}
//			}
//			if(flag){
//				if(!cnt) ans=per;
//				cnt=(cnt+1)%P;
//			}
//		}while(next_permutation(per.begin(),per.end()));
//		if(!cnt) throw Impossible();
//		printf("%d\n",cnt);
//		for(int&x:ans) printf("%d ",x);
//	}
}//namespace BF
namespace COUNT{
	int count(rang now,int x){
		if(x){
			rang sum=q[x].front();
			for(rang&a:q[x]) sum=sum+a;
			return (sum^now).len();
		}else{
			debug("counting 0,now=[%d,%d]\n",now.l,now.r);
			vector<int> vis(n,0);
			for(rang&a:q[x]){
				vis[a.l]++;
				if(a.r+1<n) vis[a.r+1]--;
			}
			for(int i=1;i<n;i++) vis[i]+=vis[i-1];
			int res=0;
			for(int i=now.l;i<=now.r;i++) res+=!!vis[i];
			return res;
		}
	}
	LL solve(){
		LL ans=1;
		int last=n;
		rang now(0,n-1);
		debug("orz\n");
		for(rang&a:q[n]) if(a!=rang(0,n-1)) throw Impossible();
		debug("orz\n");
		for(int i=n-1;i>=0;i--){
		debug("orz i=%d\n",i);
			if(i>0&&q[i].empty()) continue;
			//(last,i]
		debug("orz\n");
			if(now.empty()||now.len()<=count(now,i)||now.len()<=last-1){
				throw Impossible();
			} 
		debug("orz\n");
			red(ans*=now.len()-count(now,i));
		debug("orz\n");
			for(int j=i+1;j<last;j++) red(ans*=now.len()-j);
		debug("orz\n");
			last=i;
		debug("orz\n");
			for(rang&a:q[i]) now=now^a;
		debug("orz\n");
		}
		debug("solve() ans=%lld\n",ans);
		return ans;
	}
}//namespace COUNT
namespace CON{
	vector<int> ans;
	int fa[1<<17];
	int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
	void set(int i,int x){debug("!%d %d\n",i,x),ans[i]=x,fa[i]=i+1;debug("qweichwoiueqrhqw");}
	rang can[1<<17];
	void init(){
		ans=vector<int>(n);
		for(int i=0;i<=n+1;i++) fa[i]=i;
		rang now(0,n-1);
		for(int i=n-1;i>=0;i--){
			can[i]=now;
			if(now.empty()) throw Impossible();
			for(rang&a:q[i]) now=now^a;
		}
	}
	void set0(){
		rang now=can[0];
		vector<int> vis(n,0);
		for(rang&a:q[0]){
			vis[a.l]++;
			if(a.r+1<n) vis[a.r+1]--;
		}
		debug("orz");
		for(int i=1;i<n;i++) vis[i]+=vis[i-1];
		debug("orz");
		for(int i=now.l;i<=now.r;i++) 
			if(debug("i=%d\n",i),!vis[i]) 
				return set(i,0),debug("534155641531\n"),void();
		debug("orz\n");
		throw Impossible();
	}
	vector<int> construct(){
		debug("orz construct\n");
		for(int i=1;i<=n-1;i++){
		debug("orz\n");
			if(q[i].empty()){
				set(find(can[i].l),i);
			}else{
				rang no=q[i].front();
				for(rang&a:q[i]) no=no+a;
				if(find(can[i].l)<no.l) set(find(can[i].l),i);
				else set(find(no.r+1),i);
			}
		}
		return ans;
	}
}//namespace CON
int main(){
	#ifndef LOCAL
	 	freopen("signin.in","r",stdin);
	 	freopen("signin.out","w",stdout);
	#endif
	scanf("%d%d",&n,&m);
	for(int i=1,l,r,x;i<=m;i++) scanf("%d%d%d",&l,&r,&x),q[x].emplace_back(l-1,r-1);
	try{
		CON::init(),CON::set0();debug("ierqhgoiqerg");
		LL res=COUNT::solve();
		printf("%lld\n",res);
		for(int&x:CON::construct()) printf("%d ",x);
	}catch(Impossible){
		puts("0");
	}
	return 0;
}
posted @ 2023-03-30 17:21  caijianhong  阅读(6)  评论(0编辑  收藏  举报