题解 排列

传送门

神仙题,不会做

在经过一番愉快的猜题意之后……

发现这个限制长的很奇怪
出题人给出的思路引入大概是发现这样的话每个区间连出边来都具有传递性
然后发现等价于前后缀都有传递性
然后发现等价于 DAG 自身和补图都有传递性
然后爆搜发现有 \(n!\)
然后发现 DAG 和排列的对应关系
然后发现一个递增排列 \(p\) 通过临项交换形成一个递减序列的过程中的步骤序列对应了一个二元组排列
那么就是求上面说的这个东西了
直接枚举排列找临项交换了转移
利用康托展开可以做到一个常数较小的 \(O(n!n\log n)\)
其实也许还有一种思路引入是 \((a, c)\)\((a, b), (b, c)\) 之间直接对应到临项交换,但不是人想的所以没什么用

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 10000010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
const ll mod=998244353;
vector<pair<pair<int, int>, pair<int, int>>> limit;
inline void md(ll& a, ll b) {a+=b; a=a>=mod?a-mod:a;}

namespace force{
	int top, cnt;
	int mp[20][20];
	pair<int, int> sta[N];
	void solve() {
		for (int i=1; i<=n; ++i)
			for (int j=i+1; j<=n; ++j)
				sta[++top]={i, j};
		do {
			for (int i=1; i<=top; ++i) mp[sta[i].fir][sta[i].sec]=i;
			for (int i=1; i<=n; ++i)
				for (int j=i+1; j<=n; ++j)
					for (int k=j+1; k<=n; ++k)
						if (mp[i][j]<mp[j][k]) {if (mp[i][k]<mp[i][j] || mp[i][k]>mp[j][k]) goto jump;}
						else {if (mp[i][k]<mp[j][k] || mp[i][k]>mp[i][j]) goto jump;}
			for (auto it:limit) if (mp[it.fir.fir][it.fir.sec]>mp[it.sec.fir][it.sec.sec]) goto jump;
			// for (int i=1; i<=top; ++i) if (sta[i].fir!=sta[i].sec-1) cout<<"("<<sta[i].fir<<','<<sta[i].sec<<") "; cout<<endl;
			++cnt;
			jump: ;
		} while (next_permutation(sta+1, sta+top+1));
		cout<<cnt<<endl;
	}
}

namespace task1{
	ll f[1<<21];
	int id[30][30], tot;
	pair<int, int> rk[N];
	map<pair<int, int>, int> mp;
	int need[N], cant[N];
	void solve() {
		for (int i=1; i<=n; ++i)
			for (int j=i+1; j<=n; ++j)
				mp[{i, j}]=tot, id[i][j]=tot, rk[tot]={i, j}, ++tot;
		for (auto it:limit) {
			need[mp[{it.sec}]]|=1<<mp[{it.fir}];
			cant[mp[{it.fir}]]|=1<<mp[{it.sec}];
		}
		int lim=1<<tot;
		f[0]=1;
		for (int s=0; s<lim; ++s) {
			for (int i=0; i<tot; ++i) if (!(s&(1<<i))) {
				if ((need[i]&s)!=need[i] || (cant[i]&s)) goto jump;
				for (int a=rk[i].fir,b=a+1,c=rk[i].sec; b<c; ++b)
					if ( ! (((s&(1<<id[a][b]))?1:0)^((s&(1<<id[b][c]))?1:0)) ) goto jump;
				md(f[s|(1<<i)], f[s]);
				jump: ;
			}
		}
		cout<<f[lim-1]<<endl;
	}
}

namespace task{
	ll dp[N];
	vector<int> tab[N];
	int a[N], pos[N], fac[N], less[N];
	vector<pair<int, int>> need[12][12];
	void solve() {
		for (auto it:limit) need[it.sec.fir][it.sec.sec].pb(it.fir);
		int rk=0;
		dp[1]=1; fac[0]=1;
		for (int i=1; i<=n; ++i) fac[i]=fac[i-1]*i;
		for (int i=1; i<=n; ++i) a[i]=i;
		// do {
		// 	++rk;
		// 	for (int i=1; i<=n; ++i) tab[rk].pb(a[i]);
		// } while (next_permutation(a+1, a+n+1)) ;
		// for (int i=1; i<=n; ++i) a[i]=i; rk=0;
		do {
			++rk;
			if (!dp[rk]) continue;
			for (int i=1; i<=n; ++i) pos[a[i]]=i, less[i]=0;
			// cout<<"a: "; for (int i=1; i<=n; ++i) cout<<a[i]<<' '; cout<<endl;
			for (int i=1,v; i<n; ++i) {
				if (a[i]<a[i+1]) {
					for (auto& it:need[a[i]][a[i+1]])
						if (pos[it.fir]<pos[it.sec]) goto jump;
					v=rk;
					v-=(a[i]-1-less[a[i]])*fac[n-i]+(a[i+1]-1-less[a[i+1]]-(a[i]<a[i+1]))*fac[n-i-1];
					v+=(a[i+1]-1-less[a[i+1]])*fac[n-i]+(a[i]-1-less[a[i]]-(a[i+1]<a[i]))*fac[n-i-1];
					// cout<<"v: "; for (auto it:tab[v]) cout<<it<<' '; cout<<endl;
					md(dp[v], dp[rk]);
					jump: ;
				}
				for (int j=a[i]+1; j<=n; ++j) ++less[j];
			}
		} while (next_permutation(a+1, a+n+1));
		cout<<dp[rk]<<endl;
	}
}

signed main()
{
	freopen("perm.in", "r", stdin);
	freopen("perm.out", "w", stdout);

	n=read(); m=read();
	for (int i=1,a,b,c,d; i<=m; ++i) {
		a=read(); b=read(); c=read(); d=read();
		limit.pb({{a, b}, {c, d}});
	}
	// force::solve();
	task::solve();

	return 0;
}
posted @ 2022-03-27 21:09  Administrator-09  阅读(1)  评论(0编辑  收藏  举报