[dp] ARC160F Count Sorted Arrays

posted on 2024-10-08 11:13:49 | under | source

排列个数有足足 \(n!\) 个,无法直接做,那么可不可以将信息“压缩”起来,换种方式表示排列呢?

有套路:将排列 \(P\) 表示拆成 \(n\) 个 01 串 \(C_1\dots C_n\),其中 \(C_{i,j}=[P_j\ge i]\),那么 \(P\)\(C\) 构成双射。\(C_1\dots C_n\) 实际上可视为按 \(i\) 从小到大将 \(P_q=i\)\(q\) 点亮。

也就是说,考虑二进制数 \(S\in \{0\dots 2^n-1\}\),让 \(i\in S\)\(j\) 连有向边,其中 \(j\) 恰好比 \(i\) 多一个 \(1\),那么一条 \(0\to 2^n-1\) 的路径恰好对应一个排列。

而且 \(P\) 排好序当且仅当 \(C_1\dots C_n\) 都排好序,于是每次统计只经过排好序的点的路径总数,就是排好序的 \(P\) 的总数。简单 dp 即可,统计一次复杂度 \(O(n2^n)\)

但是本题 \(m\) 较大,\(O(mn2^n)\) 不足以通过。感性地想,真正有效的操作应该不多,有效操作 \(x,y\) 表示存在 01 串满足 \(c_x=1\)\(c_y=0\),只在有效操作时重新 dp 即可。

那么有效操作是什么量级呢?先记 \((p,q)\) 表示 \(p,q\) 是否为有效操作。现在进行有效操作 \(x,y\),我们将 01 串分为 \(A,B\) 两类,\(A\) 类满足 \(c_x=1\)\(c_y=0\)\(B\) 类则不满足。

我们将证明操作前后有效操作数量单调递减:考虑 \(A\) 类串中由无效变为有效的操作,显然只能是 \(p<x\)\(y<p\),考虑前者后者同理。那么必须有 \(a_p=1\),容易发现,原本 \((p,y)=1\) 但是操作后 \((p,y)=0\),那么恰好抵消掉了。

证明结束?上述讨论只考虑了 \(A\) 类串,那么 \(B\) 类串呢?\(B\) 产生的影响只可能是存在 \((p,y)=1\),使得操作后 \((p,y)\) 依旧不变,那么证明就错了。可是,若有 \(B\) 类串 \((p,y)=1\),那么 \(c_y=0\),又因为它是 \(B\) 类串所以 \(c_x=0\),于是会有 \((p,x)=1\),于是操作后 \((p,x)\) 也跟着不变,那么没有影响。

初始时 \(O(n^2)\) 个有效操作,于是 dp 部分复杂度 \(O(n^32^n)\)

然后拿个 vector\((p,q)=1\) 的 01 串,每次取出修改就好。假如 01 串操作后某些 \((p,q)\) 变为 \(0\),不必删掉,只需要每次取出时检查下是不是真的满足条件即可;假如 \((p,q)\) 变为 \(1\),扔进去就好。

一个 01 串产生变化的 \((p,q)\) 至少一端为 \(x,y\),数量为 \(O(n)\),而一次取出 \(O(2^n)\) 个串。每次那么该部分复杂度 \(O(n^32^n)\)

代码

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define ADD(a, b) a = (a + b)
#define chk(x) (!(x) || __builtin_ctz(x) == n - __builtin_popcount(x))
#define id(x, p) (((x) >> p) & 1)
#define ID(x, y) ((x) * n + (y))
//x=0时ctz出错 
const int N = 15;
int n, m, x, y, a[1 << N], f[1 << N], ans;
bool vis[1 << N];
vector<int> buc[N * N];

inline void ins(int a, int b, int x) {buc[ID(a, b)].push_back(x);}
inline void upd(){
	memset(f, 0, sizeof f), f[0] = 1;
	for(int i = 1; i < 1 << n; ++i)
		if(vis[i])
			for(int j = 0; j < n; ++j)
				if(id(i, j)) ADD(f[i], f[i ^ (1 << j)]);
	ans = f[(1 << n) - 1];
}
signed main(){
	cin >> n >> m;
	for(int i = 0; i < 1 << n; ++i){
		vis[i] = chk(a[i] = i);
		for(int x = 0; x < n; ++x)
			for(int y = x + 1; y < n; ++y)
				if(id(i, x) && !id(i, y)) ins(x, y, i);
	}
	upd(), ans = 1;
	while(m--){
		scanf("%lld%lld", &x, &y);
		x = (x + ans) % n, y = (y + 2 * ans) % n;
		if(x > y) swap(x, y);  
		bool fl = false;
		for(auto i : buc[ID(x, y)])
			if(id(a[i], x) && !id(a[i], y)){
				a[i] ^= (1 << x) | (1 << y);
				if(chk(a[i])) vis[i] = true, fl = true;
				else{
					for(int p = 0; p < x; ++p) if(id(a[i], p)) ins(p, x, i);
					for(int p = y + 1; p < n; ++p) if(!id(a[i], p)) ins(y, p, i);
				}
 			}
 		buc[ID(x, y)].clear();
 		if(fl) upd();
		printf("%lld\n", ans); 
	}
	return 0;
}
posted @ 2026-01-12 19:59  Zwi  阅读(1)  评论(0)    收藏  举报