Codeforces Round #737 (Div. 2)

Codeforces Round #737 (Div. 2)ios

褒貶不一,我的認爲題目質量可觀,賽時只會 A~C 而後掉大分 😞優化

A

將一個沒有重複數字的數列分爲剛好兩個非空數列,求品均值之和的最大值。spa

不難證實最大的數單獨一列是最優的。code

B

判斷是否能夠將一個沒有重複數字的數列分爲剛好 \(K\) 個非空數列,再合併,使得它升序排列。ci

一開始讀錯題了,覺得能夠拆無限次,而後 \(K\geq 3\)​ 直接 puts("Yes"),WA 了兩發 😢get

有一個比較巧妙的作法,考慮將一個數字映射爲它最終須要到的位置。string

那麼只有一個連續段是能夠分到一塊兒的,看最短段數是否 \(\leq K\)​ 便可。it

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 1e5 + 10;
int T, n, m, a[N], b[N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

bool cmp(int x, int y){return a[x] < a[y];}

int main(){
	T = read();
	while(T --){
		n = read(), m = read();
		for(int i = 1; i <= n; i ++) a[i] = read(), b[i] = i;
		sort(b + 1, b + n + 1, cmp);
		int t = 0;
		for(int i = 1; i <= n; i ++)
			if(i == 1 || b[i] != b[i - 1] + 1) t ++;
		if(t > m) puts("No");
		else puts("Yes");
	}
	return 0;
}

C

給定 \(n\)\(k\) 爲二進制數,能夠隨便填 \(0/1\),求使 \(a_1\&a_2\&\dots\&a_n\geq a_1\ {\rm xor}\ a_2\ {\rm xor}\dots\ {\rm xor}\ a_n\) 的方案數。io

分 嚴格大於 和 剛好等於 兩種狀況統計比較簡單。class

不難發現嚴格大於的狀況只出如今 \(n\) 爲偶數的時候,而且某一位上全 \(1\),前面位剛好等於,後面位隨意。

而剛好相等就是要麼某一位都爲 \(0/1\),那麼首先預處理推得:

\(M_1\):表示使 \(n\) 個位異或爲 \(0\) 的方案數 \(=\binom{n}{0}+\binom{n}{2}+\cdots\),即偶數個 \(1\)

那麼不難發現使 \(n\)\(i\) 位二進制串剛好相等的方案數爲:

  1. \(n\) 爲奇數:\((M_1+1)^i\)。加上全 \(1\) 的狀況。
  2. \(n\) 爲偶數:\((M_1-1)^i\)。減去全 \(1\) 的狀況。

\(f(i)\)​ 表示這個值,那麼對於嚴格大於的狀況(僅當 \(n\) 爲偶數時考慮):

\[S=\sum\limits_{i=1}^k f(i-1)\times (2^{k-i})^n \]

最終 \(Ans=S+f(k)\),時間複雜度 \(O(n+k\log n)\)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;
const int N = 2e5 + 10;
const LL MOD = 1e9 + 7;
int T, n, k;
LL fac[N], inv[N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

LL Pow(LL a, LL b){
	LL sum = 1;
	for(; b; b >>= 1){
		if(b & 1) sum = sum * a % MOD;
		a = a * a % MOD;
	}
	return sum;
}

LL C(int n, int m){if(n == m) return 1; return fac[n] * inv[m] % MOD * inv[n - m] % MOD;}

void Work(){
	fac[0] = 1;
	for(int i = 1; i <= n; i ++) fac[i] = fac[i - 1] * i % MOD;
	inv[n] = Pow(fac[n], MOD - 2);
	for(int i = n - 1; i >= 1; i --) inv[i] = inv[i + 1] * (i + 1) % MOD;
	LL M1 = 1;
	for(int i = 2; i <= n; i += 2) M1 = (M1 + C(n, i)) % MOD;
	LL ans = 0;
	if(!(n & 1))
		for(int i = 1; i <= k; i ++)
			ans = (ans + Pow((M1 + ((n & 1) ? 1 : - 1) + MOD) % MOD, i - 1) * Pow(Pow(2, k - i), n) % MOD) % MOD;
	ans = ((ans + Pow((M1 + ((n & 1) ? 1 : - 1) + MOD) % MOD, k)) % MOD) % MOD;
	
	printf("%lld\n", ans);
}

int main(){
	T = read();
	while(T --){
		n = read(), k = read();
		Work();
	}
	return 0;
}

D

給定 \(n\) 個串,其中某些連續段爲 \(1\) 其它爲 \(0\),若兩行之間有同列均爲 \(1\) 則它們能夠相鄰。

要求刪去一些行使得矩陣合法,求最少刪除的行數,並給出任意一組方案。

看到數據範圍就放棄 DP 的思路了,仍是要敢想 + 大力優化。

\(f(i)\) 表示第 \(i\) 行的答案,那麼 \(f(i)=\max\limits _{1\leq j<i}\{f(j)\}+1\),要求 \(i,j\) 有對應位。

利用線段樹維護列的最大值,須要支持 區間賦值 + 區間求 max,再記錄一個 \(pre_i\) 來輸出方案。

同行之間先同時 Query 再同時 Modify,防止重疊狀況的影響。時間複雜度 \(O(n\log m)\)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;

typedef pair<int, int> PII;
#define MP make_pair
#define X  first
#define Y  second

const int N = 3e5 + 10;
int n, m, t, f[N], pre[N], b[N << 1];
PII dat[N << 3], cov[N << 3], Zero = MP(0, 0);
bool vis[N];
vector<PII> G[N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

void Push_Down(int p){
	if(cov[p].X == 0) return;
	int l = p << 1, r = p << 1 | 1;
	dat[l] = max(dat[l], cov[p]), cov[l] = max(cov[l], cov[p]);
	dat[r] = max(dat[r], cov[p]), cov[r] = max(cov[r], cov[p]);
	cov[p] = MP(0, 0);
}

void Modify(int p, int l, int r, int L, int R, int v, int Pos){
	if(L <= l && r <= R){
		cov[p] = max(cov[p], MP(v, Pos));
		dat[p] = max(dat[p], MP(v, Pos));
		return;
	}
	Push_Down(p);
	int mid = (l + r) >> 1;
	if(L <= mid) Modify(p << 1, l, mid, L, R, v, Pos);
	if(R >  mid) Modify(p << 1 | 1, mid + 1, r, L, R, v, Pos);
	dat[p] = max(dat[p << 1], dat[p << 1 | 1]);
}

PII Query(int p, int l, int r, int L, int R){
	if(L <= l && r <= R) return dat[p];
	Push_Down(p);
	int mid = (l + r) >> 1;
	if(L >  mid) return Query(p << 1 | 1, mid + 1, r, L, R);
	if(R <= mid) return Query(p << 1, l, mid, L, R);
	return max(Query(p << 1, l, mid, L, R), Query(p << 1 | 1, mid + 1, r, L, R));
}

int main(){
	n = read(), m = read();
	for(int i = 1; i <= m; i ++){
		int p = read(), l = read(), r = read();
		G[p].push_back(MP(l, r));
		b[++ t] = l, b[++ t] = r;
	}
	sort(b + 1, b + t + 1);
	t = unique(b + 1, b + t + 1) - (b + 1);
	for(int i = 1; i <= n; i ++)
		for(int j = 0; j < (int) G[i].size(); j ++){
			G[i][j].X = lower_bound(b + 1, b + t + 1, G[i][j].X) - b;
			G[i][j].Y = lower_bound(b + 1, b + t + 1, G[i][j].Y) - b;
		}
	for(int i = 1; i <= n; i ++){
		f[i] = 0;
		for(int j = 0; j < (int) G[i].size(); j ++){
			PII now = Query(1, 1, t, G[i][j].X, G[i][j].Y);
			if(now.X > f[i])
				f[i] = now.X, pre[i] = now.Y;
		}
		if(!f[i])
			f[i] = 1, pre[i] = i;
		else
			f[i] ++;
		for(int j = 0; j < (int) G[i].size(); j ++)
			Modify(1, 1, t, G[i][j].X, G[i][j].Y, f[i], i);
	}
	int Mx = 0, Pos;
	for(int i = 1; i <= n; i ++) if(f[i] > Mx) Mx = f[i], Pos = i;
	for(; ; Pos = pre[Pos]) if(vis[Pos]) break; else vis[Pos] = true;
	printf("%d\n", n - Mx);
	for(int i = 1; i <= n; i ++) if(!vis[i]) printf("%d ", i);
	puts("");
	return 0;
}

E

國際象棋,要求用一個 Queen\(\leq 130\) 步內將死一個 King

其中 (hidden)King 的位置未知,可是它不能吃 Queen

聽說 S 型亂搞就能過 System Test 😮

考慮一行行往下壓,直到 King 無處可走。

首先思考一下,若從左往右將一行掃一遍的時候,King 並無上下移動,那麼他確定離 Queen 至少兩行。

而後分解步驟:

  1. 將一行掃一遍。
  2. King 下移就直接下移。
  3. King 全程沒有垂直移動就直接下移。
  4. King 上移,它至多移動到 Queen 下面一行,再重新掃一遍,就可以強制他向下移動。

上一操做的次數有限,不難發現至多 \(8\) 次,每次操做掃描須要 \(8\) 步。(將後移動到最左邊也算一步)

每次換行也須要一步,至多 \(7\) 次換行,因此總次數 \(\leq 8(8+8)=128<130\)

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

int nowY;

string Move(int x, int y){
	cout << x << ' ' << y << endl;
	nowY = y;
	string s;
	cin >> s;
	return s;
}

bool Get(int i){
	string s;
	for(int j = (nowY == 1) ? 2 : 1; j <= 8; j ++){
		s = Move(i, j);
		if(s == "Done")
			return true;
		if(s.find("Up") != string::npos)
			return Get(i);
		if(s.find("Down") != string::npos)
			return false;
	}
	return false;
}

void Work(){
	nowY = 1;
	string s;
	for(int i = 1; i <= 8; i ++){
		s = Move(i, nowY);
		if(s == "Done")
			return;
		if(Get(i))
			return;
	}
}

int main(){
	int T; scanf("%d", &T);
	while(T --) Work();
	return 0;
}
相關文章
相關標籤/搜索