"蔚来杯"2022牛客暑期多校训练营5 题解

C. Bit Transmission

记录每一位上的YES/NO个数,发现若有一位 ① 没被问到 或者 ② YES个数等于NO个数,则直接输出-1。

然后枚举每一位询问次数超过3的位置,若发现同时存在YES/NO,则这个位置会产生错误。如果发现两处及以上错误,则不满足题目条件,直接输出-1。

如果只有一个错误或者没有错误,那么我们直接输出即可。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int MAXN = 2e6 + 5;

int cnt[MAXN], check[MAXN], ans[MAXN], n;

void Solve() {
	for(int i = 1; i <= n; i++) cnt[i] = check[i] = 0, ans[i] = -1;
	bool cur = 1;
    for(int i = 1; i <= 3 * n; i++) {
		int id, flag; string S;
		cin >> id >> S; ++id;
		if(id > n) continue;
		if(S == "YES") flag = 1;
		else if(S == "NO") flag = -1;
		cnt[id]++;
		check[id] += flag;
	}
    if(!cur) return void(cout << "-1\n");
	for(int i = 1; i <= n; i++) if(!cnt[i] || !check[i]) return void(cout << "-1\n");
	int wa = 0;
	for(int i = 1; i <= n; i++) {
		if(cnt[i] >= 3) {
            if( cnt[i] - abs(check[i]) > 2 ) return void(cout << "-1\n");
 			if( abs(check[i]) < cnt[i] ) {
				if(wa) return void(cout << "-1\n");
				else wa = i;
			}
		}
	}
    for(int i = 1; i <= n; i++) ans[i] = (check[i] > 0 ? 1 : 0);
	for(int i = 1; i <= n; i++) cout << ans[i] << ""; cout << "\n";
}

signed main() 
{
	while(cin >> n) Solve();
	return 0;
}

D. Birds in the tree

分别枚举叶子节点为0,1的情况。

\(dp[1][u], \ dp[2][u]\) 分别表示不限制/限制节点 \(u\) 的类别的方案数,设 \(v\)\(u\) 的儿子, \(l \in \{0,1\}\) ,有

\[当S[u] = l时,dp[1][u] = dp[2][u] = \prod (dp[1][v] + 1) \\ 当S[u] ≠ l时,dp[1][u] = \prod (dp[1][v] + 1) - 1, dp[2][u] = dp[1][u] - \sum dp[1][v] \]

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int MAXN = 3e5 + 5;
const int MOD = 1e9 + 7;

int n, dp[3][MAXN];
string S;
vector<int> G[MAXN];

void DFS(int u, int f, int l) {
	dp[1][u] = 1; int sum = 0;
	for(auto v : G[u]) {
		if(v == f) continue;
		DFS(v, u, l);
		dp[1][u] = dp[1][u] * (dp[1][v] + 1) % MOD;
		sum = (sum + dp[1][v]) % MOD;
	}
	if(S[u] - '0' == l) dp[2][u] = dp[1][u];
	else dp[1][u]--, dp[2][u] = (dp[1][u] - sum + MOD) % MOD;
}

signed main() 
{
	cin >> n >> S; S = " " + S;
	for(int i = 1; i < n; i++) {
		int u, v; cin >> u >> v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	int ans = 0;
	DFS(1, 0, 1);
	for(int i = 1; i <= n; i++) ans = (ans + dp[2][i]) % MOD, dp[1][i] = dp[2][i] = 0;
	DFS(1, 0, 0);
	for(int i = 1; i <= n; i++) ans = (ans + dp[2][i]) % MOD, dp[1][i] = dp[2][i] = 0;
	cout << ans << "\n";	
	return 0;
}

E. Fraction Game

洛谷原题

\(st[i][j][l]\) 表示三角形顶点坐标 \((i,j)\),边长 \(2^l\) 时的最大值。转移方程为:

\[st[i][j][l] = \max \{st[i][j][l-1], \max_{y=j}^{j+2^{l-1}}\{st[i+2^{l-1}][y][l-1]\}\} \]

图示如洛谷原题第一篇题解。

注意到时间空间都有可能爆炸。

在空间上,我们发现第三维的 \(l\) 只和 \(l-1\) 有关,可以将第三维优化掉,空间复杂度 \(O(n^2)\)

在时间上,我们发现固定 \(i\) 从小到大枚举 \(j\) 的时候,所得max的区间 \([j,j+2^{l-1}]\) 会向右滚动,利用单调队列优化即可,时间复杂度 \(O(n^2 \log k)\)

//#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
#define pii pair<int, int>
#define fi first
#define se second
using namespace std;

const int MAXN = 1005;

int n, K;
pii st[2][MAXN][MAXN];
deque<int> Q;

pii operator + (const pii& a, const pii& b) {
	pii res = {0, 1};
	res.se = a.se * b.se;
	res.fi = a.se * b.fi + b.se * a.fi;
	int g = __gcd(res.fi, res.se);
	res.fi /= g, res.se /= g;
	return res;
}

pii operator - (const pii& a, const pii& b) {
	pii res = {0, 1};
	res.se = a.se * b.se;
	res.fi = b.se * a.fi - b.fi * a.se;
	int g = __gcd(res.fi, res.se);
	res.fi /= g, res.se /= g;
	if(res.fi * res.se < 0) {
		res.fi = abs(res.fi);
		res.se = abs(res.se);
		res.fi *= -1;
	}
	return res;
}

bool operator < (const pii& a, const pii& b) {
	pii res = a - b;
	if(res.fi < 0) return 1;
	else return 0;
}

bool operator > (const pii& a, const pii& b) {
	pii res = a - b;
	if(res.fi > 0) return 1;
	else return 0;
}

pii max(pii a, pii b) {
	pii res = a - b;
	if(res.fi > 0) return a;
	else return b;
}

pii min(pii a, pii b) {
	pii res = a - b;
	if(res.fi < 0) return a;
	else return b;
}

signed main()
{
	scanf("%lld%lld", &n, &K);
	for(int i = 1; i <= n; i++) for(int j = 1; j <= i; j++) scanf("%lld/%lld", &st[0][i][j].fi, &st[0][i][j].se);
	int l;
	for(l = 1; (1 << l) <= K; l++) {
		for(int i = 1; i + (1 << l) - 1 <= n; i++) {
			int x = i + (1 << l - 1);
			for(int j = 1; j <= (1 << l - 1); j++) {
				while(!Q.empty() && st[0][x][ Q.back() ] < st[0][x][j]) Q.pop_back(); 
				Q.push_back(j);
			}
			for(int j = 1; j <= i; j++) {
				int y = j + (1 << l - 1);
				while(!Q.empty() && Q.front() < j) Q.pop_front();
				while(!Q.empty() && st[0][x][ Q.back() ] < st[0][x][y]) Q.pop_back(); 
				Q.push_back(y);
				st[1][i][j] = max(st[0][i][j], st[0][x][ Q.front() ]);
			}
			while(!Q.empty()) Q.pop_back();
		}
		
		for(int i = 1; i <= n; i++) for(int j = 1; j <= i; j++) st[0][i][j] = st[1][i][j], st[1][i][j] = {0, 1};
	}
	l--;
	if((1 << l) < K) {
		for(int i = 1; i + K - 1 <= n; i++) {
			int x = i + K - (1 << l);
			for(int j = 1; j <= K - (1 << l); j++) {
				while(!Q.empty() && st[0][x][ Q.back() ] < st[0][x][j]) Q.pop_back(); 
				Q.push_back(j);
			}
			for(int j = 1; j <= i; j++) {
				int y = j + K - (1 << l);
				while(!Q.empty() && Q.front() < j) Q.pop_front();
				while(!Q.empty() && st[0][x][ Q.back() ] < st[0][x][y]) Q.pop_back(); 
				Q.push_back(y);
				st[1][i][j] = max(st[0][i][j], st[0][x][ Q.front() ]);
			}
			while(!Q.empty()) Q.pop_back();
		}
		
		for(int i = 1; i <= n; i++) {
			for(int j = 1; j <= i; j++) st[0][i][j] = st[1][i][j], st[1][i][j] = {0, 1};
		}
	}
	pii ans = {0, 1};
	for(int i = 1; i + K - 1 <= n; i++) {
		for(int j = 1; j <= i; j++) ans = (ans + st[0][i][j]);
	}
	printf("%lld/%lld", ans.fi, ans.se);
	return 0;
}

G. KFC Crazy Thursday

manacher求出每个下标 \(i\) 的最大拓展长度 \(f[i]\),对于每个 \([i, i+f[i]]\) 中的特定字符,其在里面出现的位置都可作为一个回文串的结尾,前缀和维护即可。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int MAXN = 2e6 + 5;
const int BASE = 27;
const int MOD = 1e9 + 7;

char T[MAXN], S[MAXN];
int sum[MAXN][28], n, f[MAXN];

void Manacher() {
	
	T[0] = '#', T[1] = '$';
	for(int i = 1; i <= n; i++) T[i << 1] = S[i], T[i << 1 | 1] = '$';
	n = n << 1 | 1;
	for(int i = 0; i <= n; i++) S[i] = T[i];
	for(int i = 0; i <= n; i++) f[i] = 0;
	
	int maxR = 0, pos = 0;
	for(int i = 1; i <= n; i++) {
		if(i < maxR) f[i] = min(f[pos * 2 - i], maxR - i);
		while(i - f[i] - 1 > 0 && i + f[i] + 1 <= n && S[i + f[i] + 1] == S[i - f[i] - 1]) f[i]++;
		if(i + f[i] > maxR) maxR = i + f[i], pos = i;
	}
}

void Solve() {
	Manacher();
	int ans1 = 0, ans2 = 0, ans3 = 0;
	for(int i = 1; i <= n; i++) {
		for(int j = 0; j < 26; j++) sum[i][j] = sum[i - 1][j];
		if(S[i] >= 'a' && S[i] <= 'z') sum[i][ S[i] - 'a' ]++;
	}
	for(int i = 1; i <= n; i++) {
		ans1 += sum[ i + f[i] ]['k' - 'a'] - sum[ i - 1 ]['k' - 'a'];
		ans2 += sum[ i + f[i] ]['f' - 'a'] - sum[ i - 1 ]['f' - 'a'];
		ans3 += sum[ i + f[i] ]['c' - 'a'] - sum[ i - 1 ]['c' - 'a'];
	}
	cout << ans1 << " " << ans2 << " " << ans3 << "\n";
}

signed main() 
{
	while(cin >> n) {
		cin >> S + 1;
		Solve();
	}
	return 0;
}
posted @ 2022-08-02 22:00  Orzjh  阅读(29)  评论(0)    收藏  举报