题解 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;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/solution-SS230329A.html