\(\text{E - Packing Under Range Regulations}\)

解法

将左端点从小到大排序,把对应的右端点插入优先队列。然后枚举盒子 \(i\) 放入当前优先队列第一个元素,删除后判断优先队列里第一个元素能否放入 \(i+1\) 之后的盒子。最后插入新的左端点对应右端点。

这样保证可以放置的情况下,右端点顺序是递增的。

代码

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' or s<'0')
		f|=(s=='-');
	while(s>='0' and s<='9')
		x=(x<<1)+(x<<3)+(s^48),
		s=getchar();
	return f?-x:x;
}

template <class T>
inline void write(const T x) {
	if(x<0) {
		putchar('-'),write(-x);
		return;
	}
	if(x>9) write(x/10);
	putchar(x%10^48);
}

#include <map>
#include <queue>
#include <vector>
using namespace std;

const int maxn=2e5+5;

priority_queue < int,vector<int>,greater<int> > q;
int n,idx;
map <int,int> mp;
vector <int> g[maxn];

int main() {
	int x,y;
	for(int T=read(9);T;--T) {
		n=read(9); mp.clear();
		while(!q.empty()) q.pop();
		idx=0;
		for(int i=1;i<=n;++i) {
			x=read(9),y=read(9);
			if(!mp.count(x)) {
				mp[x]=++idx;
				g[idx].clear();
			}
			g[mp[x]].push_back(y);
		}
		mp[1e9+1]=1;
		int i=mp.begin()->first;
		bool ans=0;
		while(i<=1e9) {
			x=mp[i];
			for(int j=0;j<g[x].size();++j)	
				q.push(g[x][j]);
			q.pop();
			if(q.empty())
				i=mp.lower_bound(i+1)->first;
			else {
				if(q.top()<=i) {
					ans=1; break;
				}
				++i;
			}
		}
		if(!q.empty()) ans=1;
		puts(ans?"No":"Yes");
	}
	return 0;
}

\(\text{G - Three Permutations}\)

解法

前几天才做了夫妻围坐问题… 但是还是不会做。令人生草。关于夫妻围坐问题可以翻翻 "精尽人亡" 这场比赛,但是目前状态是咕咕咕。

首先可以自然地联想到容斥原理,还是令 \(A_i\) 为 "满足 \(i\) 位置上是 \(p_i\)\(q_i\)" 的方案集合。如果将 \((p_i,q_i)\) 连边最后生成一个环的话就是原问题,但实际上本题可能生成多个环。不过很好的一点是一个连通块也只能是环,因为每个点的入度与出度都是 \(1\)

所以问题实际上可以分解为:对每个环求解 \(\sum_{1\le i_1<i_2<...<i_k\le n} |A_{i_1}\cap A_{i_2}\cap ...\cap A_{i_k}|\),然后 \(\mathcal O(n^2)\)\(\mathtt{dp}\) 把答案拼起来。

对于大小为 \(cnt\) 的环,我们发现第 \(i\) 条边与 \(p_i\) 的交界可以视作 \(i\) 位置上是 \(p_i\)\(q_i\) 同理。而且假设与 \(p_i\) 相邻的另一条边是 \(j\)(显然 \(p_i=q_j\)),那么 "满足 \(i\) 位置上是 \(p_i\)" 时,"满足 \(i\) 位置上是 \(q_i\)" 和 "满足 \(j\) 位置上是 \(q_j\)" 都是不可能成立的。转化到环上就是选取了第 \(i\) 条边与 \(p_i\) 的交界时,第 \(i\) 条边与 \(q_i\) 的交界和第 \(j\) 条边与 \(q_j\) 的交界都是不能选的。这就转化成了原问题。

"交界" 有 \(2\cdot cnt\) 个,假设选 \(k\) 个条件。套用公式可得方案数为 \(\binom{2\cdot cnt-k-1}{k-1}\cdot \frac{2\cdot cnt}{k}\cdot (n-k)!\)。如果懒得求 \(k\) 的逆元以及减去 \(\log\) 的求逆元复杂度,可以转化成这个式子:\(\left ( \binom{2\cdot cnt-k}{k}+\binom{2\cdot cnt-k-1}{k-1}\right )\cdot (n-k)!\)

自环需要特判一下。

代码

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' or s<'0')
		f|=(s=='-');
	while(s>='0' and s<='9')
		x=(x<<1)+(x<<3)+(s^48),
		s=getchar();
	return f?-x:x;
}

template <class T>
inline void write(const T x) {
	if(x<0) {
		putchar('-'),write(-x);
		return;
	}
	if(x>9) write(x/10);
	putchar(x%10^48);
}

const int mod=1e9+7,maxn=3005;

int n,a[maxn],b[maxn],to[maxn];
int fac[maxn<<1],ifac[maxn<<1];
int tmp[maxn],dp[maxn];
bool vis[maxn];

int inv(int x,int y=mod-2) {
	int r=1;
	while(y) {
		if(y&1) r=1ll*r*x%mod;
		x=1ll*x*x%mod; y>>=1;
	}
	return r;
}

void init() {
	fac[0]=1;
	for(int i=1;i<=(n<<1);++i)
		fac[i]=1ll*fac[i-1]*i%mod;
	ifac[n<<1]=inv(fac[n<<1]);
	for(int i=(n<<1)-1;i>=0;--i)
		ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
}

int C(int n,int m) {
	if(n<m or n<0 or m<0)
		return 0;
	return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}

signed main() {
	n=read(9); init();
	for(int i=1;i<=n;++i)
		a[i]=read(9);
	for(int i=1;i<=n;++i)
		b[i]=read(9),
		to[a[i]]=b[i];
	dp[0]=1;
	for(int i=1;i<=n;++i) {
		if(vis[i]) continue;
		int cnt=0;
		for(int j=i;!vis[j];j=to[j]) {
			++cnt;
			vis[j]=1;
		}
		for(int j=0;j<=n;++j)
			tmp[j]=0;
		for(int j=0;j<=cnt;++j) {
			int inc=(C(cnt*2-j,j)+C(cnt*2-j-1,j-1))%mod;
			if(cnt==1 and j==1) inc=1;
			for(int k=0;k<=n-j;++k)
				tmp[j+k]=(tmp[j+k]+1ll*dp[k]*inc%mod)%mod;
		}
		for(int j=0;j<=n;++j)
			dp[j]=tmp[j];
	}
	int ans=0;
	for(int i=0;i<=n;++i) {
		int t=1ll*fac[n-i]*dp[i]%mod;
		if(i&1) ans=(ans-t+mod)%mod;
		else ans=(ans+t)%mod;
	}
	print(ans,'\n');
	return 0;
}
posted on 2021-08-18 21:40  Oxide  阅读(69)  评论(0编辑  收藏  举报