test2024.3.21

小超手123發表於2024-03-27

多邊形

題意:

有一個長度為 \(n\)\(0/1\) 序列,有 \(m\) 次操作 \(u_{i},v_{i}\),若 \(a_{u_{i}}=1,a_{v_{i}}=0\) 則交換。

詢問對於 \(1,2,\dots,n\) 中的每個 \(k\),有多少種初始狀態,滿足恰好有 \(k\)\(1\),並且經過 \(m\) 次操作後,所有 \(1\) 形成了一個區間。答案對 \(2\) 取模。

\(n \le 35,m \le 1000\)

分析:

顯然要搜尋。

對於每個位置用 \(0 /1 /2\) 記錄,分別表示確定為 \(0\)、確定為 \(1\)、沒確定。

對於一個操作 \(u,v\),簡單分討一下:

  • \(u=v=2\),若 \(u \ne v\),則操作後一定為 \(0,1\),這樣的有兩種情況,因此不用處理。若 \(u=v\),則會產生 \(1,1\)\(0,0\) 兩個分支。
  • \(u,v\) 恰好有一個為 \(2\)
  • \(u,v\) 沒有 \(2\)

搜完 \(m\) 次操作後隨便處理一下。時間複雜度為 \(O(2^{\frac{n}{2}} n^2)\)\(O(2^{\frac{n}{2}} n)\),事實上完全跑不滿。

程式碼:
#include<bits/stdc++.h>
#define int long long
#define N 1010
using namespace std;
int n, m;
int U[N], V[N], now[N], ans[N];
void dfs(int dep) {
	if(dep == m + 1) {
		//for(int i = 1; i <= n; i++) cout << now[i] << " ";
		//cout << endl;
		int L = 0;
		for(int i = 1; i <= n; i++)
			if(now[i] == 1) {
				L = i;
				break;
			}
		if(L == 0) {
			for(int Pos = 1; Pos <= n; ) {
				if(!now[Pos]) {
					Pos++;
					continue;
				}
				int R = Pos - 1;
				while(now[R + 1] == 2 && R < n) R++;
				for(int u = Pos; u <= R; u++)
				for(int v = u; v <= R; v++) ans[v - u + 1]++;
				Pos = R + 1;
			}
			return;
		}
		int R = L;
		for(int i = L + 1; i <= n; i++) {
			if(now[i] == 0) break;
			if(now[i] == 1) R = i;
		}
			
			
		for(int i = R + 1; i <= n; i++)	
			if(now[i] == 1) return;
		//[L, R]
		int l = L, r = R;
		while(now[l] != 0 && l > 0) l--;
		while(now[r] != 0 && r <= n) r++;
		l++; r--;
		//cout << l << " , " << L << "   " << R << " , " << r << endl;
		for(int u = l; u <= L; u++)
		for(int v = R; v <= r; v++) ans[v - u + 1]++;
		return;
	}
	int u = now[U[dep]], v = now[V[dep]];
	if(u == 2 && v == 2) {
		now[U[dep]] = 1;
		now[V[dep]] = 1;
		dfs(dep + 1);
		now[U[dep]] = 0;
		now[V[dep]] = 0;
		dfs(dep + 1);
		now[U[dep]] = 2;
		now[V[dep]] = 2;
	}
	else if(u == 2 || v == 2) {
		if(u == 1 && v == 2) {
			swap(now[U[dep]], now[V[dep]]);
			dfs(dep + 1);
			swap(now[U[dep]], now[V[dep]]);
		}
		else if(u == 2 && v == 1) {
			dfs(dep + 1);
		}
		else if(u == 2 && v == 0) {
			swap(now[U[dep]], now[V[dep]]);
			dfs(dep + 1);
			swap(now[U[dep]], now[V[dep]]);
		}
		else if(u == 0 && v == 2) {
			dfs(dep + 1);
		}
	}
	else {
		if(u == 1 && v == 0) swap(now[U[dep]], now[V[dep]]);
		dfs(dep + 1);
		if(u == 1 && v == 0) swap(now[U[dep]], now[V[dep]]);
	}
}
signed main() {
	cin >> n >> m;
	for(int i = 1; i <= m; i++) cin >> U[i] >> V[i];
	for(int i = 1; i <= n; i++) now[i] = 2;
	dfs(1);
	for(int i = 1; i <= n; i++) cout << ans[i] % 2 << " ";
	return 0;
}

倒水

題意:

分析:

\(cnt_{i}\) 表示能到達 \(i\) 的平臺的個數(包括本身),那麼答案顯然為 \(\sum_{i=1}^{n} \frac{1}{cnt_{i}}\)。因為 \(cnt_{i}\) 個數隨意排列,\(i\) 在最後一個位置的機率為 \(\frac{cnt_{i}}{1}\)

考慮計算 \(cnt\):對於平臺 \(u\) 而言,高度從低往高更新,如果 \(v\)\(u\) 有交且不是 \(v\) 包含 \(u\),那麼 \(u \leftarrow u \cup v\)。最後 \(cnt\) 就是被 \(u\) 覆蓋的區間。

\(L2_{i},R2_{i}\) 分別表示覆蓋 \(i\) 的左右端點的在 \(i\) 平臺上面的高度最低的平臺。

如何找天花板呢?記 \(L1_{i},R1_{i}\) 分別表示 \(i\) 平臺的左右端點能覆蓋到的在 \(i\) 下面高度最高的平臺。

再記一個 \(Go_{i}\) 表示 \(i\) 能跳到最高的平臺,那麼 \(i\) 從大到小列舉,\(Go_{L1_{i}} \leftarrow Go_{i},Go_{R1_{i}} \leftarrow Go_{i}\)

\(Go_{i}\) 透過 \(L2_{i}\) 往上跳一步就是平臺。

知道了平臺的高度就可以很套路地使用倍增計算這個圖形所圍成的平臺的個數。

時間複雜度 \(O(n \log n)\)

程式碼:
#include<bits/stdc++.h>
#define int long long
#define N 1000005
#define mod 1000000007
using namespace std;
int n;
int Pow(int a, int n) {
	if(n == 0) return 1;
	if(n == 1) return a;
	int x = Pow(a, n / 2);
	if(n % 2 == 0) return x * x % mod;
	else return x * x % mod * a % mod;
}
int inv(int x) {
	return Pow(x, mod - 2);
}
int c[N * 4], tag[N * 4];
void maketag(int u, int x) {
	c[u] = x;
	tag[u] = x;
}
void pushdown(int u) {
	if(!tag[u]) return;
	maketag(u * 2, tag[u]);
	maketag(u * 2 + 1, tag[u]);
	tag[u] = 0;
}
void update(int u, int L, int R, int l, int r, int x) {
	if(R < l || r < L) return;
	if(l <= L && R <= r) {
		maketag(u, x);
		return;
	}
	int mid = (L + R) / 2;
	pushdown(u);
	update(u * 2, L, mid, l, r, x);
	update(u * 2 + 1, mid + 1, R, l, r, x);
}
int query(int u, int L, int R, int x) {
	if(L == R) return c[u];
	int mid = (L + R) / 2;
	pushdown(u);
	if(x <= mid) return query(u * 2, L, mid, x);
	else return query(u * 2 + 1, mid + 1, R, x);
}
int t[N];
int lowbit(int x) {
	return x & (-x);
}
void add(int x, int y) {
	for(int i = x; i <= 2 * n; i += lowbit(i)) 
		t[i] += y;
}
int query(int x) {
	int res = 0;
	for(int i = x; i; i -= lowbit(i))
		res += t[i];
	return res;
}
int ask(int L, int R) {
	if(L > R) return 0;
	return query(R) - query(L - 1);
}
int ans, l[N], r[N], Go[N]; //Go[i]表示i最高跳到的平臺
int L1[N], R1[N]; //表示i能直接流到的高度最大的平臺
int L2[N], R2[N]; //表示i頭上與它有交集的高度最低的平臺
int L[N][22], R[N][22], SL[N][22], SR[N][22];
struct node {
	int ll, rr, h;
}a[N];
bool cmp(node x, node y) {
	return x.rr < y.rr;
}
bool cmp2(node x, node y) {
	return x.ll < y.ll;
}
void init() {
	for(int i = 1; i <= n; i++) {
		L1[i] = query(1, 1, 2 * n, l[i]);
		R1[i] = query(1, 1, 2 * n, r[i]);
		update(1, 1, 2 * n, l[i], r[i], i);
	}
	for(int i = n; i >= 1; i--) {
		Go[L1[i]] = max(Go[L1[i]], Go[i]);
		Go[R1[i]] = max(Go[R1[i]], Go[i]);			
	}
	update(1, 1, 2 * n, 1, 2 * n, n + 1);
	for(int i = n; i >= 1; i--) {
		L[i][0] = L2[i] = query(1, 1, 2 * n, l[i]);
		R[i][0] = R2[i] = query(1, 1, 2 * n, r[i]);
		update(1, 1, 2 * n, l[i], r[i], i);
		//cout << i << " : " << L[i][0] << " , " << R[i][0] << endl;
	}
	sort(a + 1, a + n + 1, cmp);
	for(int i = 1; i <= n; i++) {
		//cout << a[i].h << endl;
		add(a[i].h, 1);
		SR[a[i].h][0] = ask(a[i].h, R[a[i].h][0]);
	}
	
	sort(a + 1, a + n + 1, cmp2);
	memset(t, 0, sizeof(t));
	for(int i = 1; i <= n; i++) {
		add(a[i].h, 1);
		SL[a[i].h][0] = ask(a[i].h + 1, L[a[i].h][0] - 1);
	}
	//for(int i = 1; i <= n; i++) cout << i << " : " << SL[i][0] << endl;
}
signed main() {
	//freopen("ex_water3.in", "r", stdin);
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	cin >> n;
	for(int i = 1; i <= n; i++) {
		cin >> l[i] >> r[i];
		Go[i] = i;
		a[i].ll = l[i];
		a[i].rr = r[i];
		a[i].h = i;
	} 
	init();
	for(int j = 1; j <= 20; j++) {
		for(int i = 1; i <= n; i++) {
			L[i][j] = L[L[i][j - 1]][j - 1];
			R[i][j] = R[R[i][j - 1]][j - 1];
			SL[i][j] = SL[i][j - 1] + SL[L[i][j - 1]][j - 1];
			SR[i][j] = SR[i][j - 1] + SR[R[i][j - 1]][j - 1];
		}
	}
	for(int i = 1; i <= n; i++) {
		int cnt = 0, x = i;
		for(int dep = 20; dep >= 0; dep--) {
			if(R[x][dep] != 0 && R[x][dep] <= L[Go[i]][0]) {
				cnt += SR[x][dep];
				x = R[x][dep];
			}
		}
		x = i;
		for(int dep = 20; dep >= 0; dep--) {
			if(L[x][dep] != 0 && L[x][dep] <= L[Go[i]][0]) {
				cnt -= SL[x][dep];
				x = L[x][dep];
			}
		}
		ans = (ans + inv(cnt)) % mod;
	}
	cout << ans;
	return 0;
}
/*
5
2 9
3 4
1 8
6 10
5 7
*/