Codeforces Round #778 (Div. 1 + Div. 2)

CF1654A Maximum Cake Tastiness

显然是最大值和次大值和,因为我总可以把任两个数换到一起。

CF1654B Prefix Removals

首字母在后面出现过就一定会被删,否则一定不会。

CF1654C Alice and the Cake

很轻松可以求出原蛋糕大小,然后按题意模拟切糕,切出来的如果条件中有就匹配掉,否则继续切,切成 \(n\) 块后还没匹配完肯定无解。

const int MAXN = 2e5 + 5;

int a[MAXN]; 

multiset <LL> S;

int main() {
	int T;read(T);
	while(T -- > 0) {
		int n;read(n);
		LL num = 0;
		S.clear();
		for (int i = 1; i <= n; ++i) read(a[i]) , num += a[i] , S.insert(a[i]);
		queue <LL> q;q.push(num);
		int res = n - 1; 
		while(!q.empty()) {
			LL cur = q.front();
			q.pop();
			auto it = S.lower_bound(cur);
			if(it != S.end() && (*it) == cur) {
				S.erase(it);
				continue;
			}
			res --;if(res < 0) break;
			q.push(cur / 2) , q.push((cur + 1) / 2);
		}
		if(res < 0) puts("NO");
		else puts("YES");
	}
	return 0;
}

CF1654D Potion Brewing Class

线性筛,\(\log n\) 时间复杂度分解质因数,存在 set 里面模拟,算出 1 的最小值,然后模拟即可。

const int MAXN = 2e5 + 5;
const LL mod = 998244353;
 
int head[MAXN] , to[MAXN << 1] , nxt[MAXN << 1] , cnt;
pll edge[MAXN << 1];
void add(int u , int v , pll w) {nxt[++cnt] = head[u];head[u] = cnt;to[cnt] = v;edge[cnt] = w;}
 
int Is_pr[MAXN] , prim[MAXN] , num , Min[MAXN];
void pre() {
	int N = 2e5;
	for (int i = 2; i <= N; ++i) {
		if(!Is_pr[i]) prim[++num] = i , Min[i] = i;
		for (int j = 1; j <= num && prim[j] * i <= N; ++j) {
			Is_pr[prim[j] * i] = 1;
			Min[prim[j] * i] = prim[j];
			if(i % prim[j] == 0) break;
		}
	}
}
 
int n;
 
map <LL , LL> cur , Ans;
 
LL qpow(LL a , LL b) {
	LL res = 1;
	while(b) {
		if(b & 1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}
 
void dfs(int x , int fa) {
	for (int i = head[x]; i; i = nxt[i]) {
		int v = to[i];
		if(v == fa) continue;
		LL X = edge[i].fs , y = edge[i].sc;
		
		while(X > 1) {
			LL tmp = Min[X];
			LL tot = 0;
			while(X % tmp == 0) X /= tmp , tot ++;
			cur[tmp] -= tot;
		}
		
		while(y > 1) {
			LL tmp = Min[y];
			LL tot = 0;
			while(y % tmp == 0) y /= tmp , tot ++;
			cur[tmp] += tot;
			Ans[tmp] = max(Ans[tmp] , cur[tmp]);
		}
		
		dfs(v , x);
		
		X = edge[i].fs , y = edge[i].sc;
		
		while(X > 1) {
			LL tmp = Min[X];
			LL tot = 0;
			while(X % tmp == 0) X /= tmp , tot ++;
			cur[tmp] += tot;
		}
		
		while(y > 1) {
			LL tmp = Min[y];
			LL tot = 0;
			while(y % tmp == 0) y /= tmp , tot ++;
			cur[tmp] -= tot;
		}
	}
}
 
LL ans;
void calc(int x , int fa , LL now) {
	ans = (ans + now) % mod;
	for (int i = head[x]; i; i = nxt[i]) {
		int v = to[i];
		if(v == fa) continue;
		calc(v , x , now * edge[i].fs % mod * qpow(edge[i].sc , mod - 2) % mod);
	}
}
 
int main() {
	int T;
	read(T);
	pre();
	while(T -- > 0) {
		read(n);
		for (int i = 1; i < n; ++i) {
			int u , v , x , y;
			read(u),read(v),read(x),read(y);
			add(u , v , mp(y , x));
			add(v , u , mp(x , y));
		}
		cur.clear() , Ans.clear();
		dfs(1 , 0);
		LL res = 1;
		for (auto it = Ans.begin(); it != Ans.end(); ++it) {
			LL x = (it)->fs , y = (it)->sc;
			if(y) res = res * qpow(x , y) % mod;
		}
		ans = 0;
		calc(1 , 0 , res);
		write(ans);
		cnt = 0;
		for (int i = 1; i <= n; ++i) head[i] = 0;
	}
	return 0;
}

CF1654E Arithmetic Operations

写了个大常数。

注意到值域的特殊性。

考虑枚举公差,如果公差很大,那么显然只会在 \(\frac{V}{d}\) 的范围内有数不变。因此根号分治,公差大的部分跑 dp,公差小的依次枚举公差处理。

const int MAXN = 1e5 + 5;
const int N = 1e5;
const int S = 500;
 
int a[MAXN] , n;
 
unordered_map <int , int> dp[MAXN];
 
int cur[MAXN * S * 3];
 
int Abs(int x) {
	return x < 0 ? -x:x; 
}
 
int main() {
	read(n);
	for (int i = 1; i <= n; ++i) read(a[i]);
	int ans = n;
	
	for (int d = 0; d <= S; ++d) {
		for (int i = 1; i <= n; ++i) cur[a[i] - d * i + N * S] ++ , ans = min(ans , n - cur[a[i] - d * i + N * S]);
		for (int i = 1; i <= n; ++i) cur[a[i] - d * i + N * S] --;
		
		for (int i = 1; i <= n; ++i) cur[a[i] + d * i + N * S] ++ , ans = min(ans , n - cur[a[i] + d * i + N * S]);
		for (int i = 1; i <= n; ++i) cur[a[i] + d * i + N * S] --;
	}
	
	for (int i = n; i >= 1; --i) {
		for (int j = 1; j <= S && i + j <= n; ++j) {
			if((a[j + i] - a[i]) % j == 0) {
				int d = (a[j + i] - a[i]) / j;
				if(Abs(d) <= S) continue;
				if(dp[j + i].count(d)) dp[i][d] = max(dp[i][d] , dp[j + i][d] + 1) , ans = min(ans , n - dp[i][d]);
				else dp[i][d] = max(dp[i][d] , 2) , ans = min(ans , n - dp[i][d]);
			}
		}
	}
	
	write(ans);
	return 0;
}

CF1654F Minimal String Xoration

见过的最易 F 题。

很容易想到倍增,模仿后缀排序。

理论依据是 \((i \oplus (j \oplus 2^k) = ((i \oplus 2^k) \oplus j)\)。(好蠢啊)

const int MAXN = (1 << 18) + 5;
char s[MAXN];
int rk[MAXN] , cnt[MAXN] , x[MAXN] , sa[MAXN];
 
int main() {
	int n;
	read(n);
	scanf("%s" , s);
	
	int m = 128;
	for (int i = 0; i < (1 << n); ++i) cnt[rk[i] = s[i]] ++;
	for (int i = 1; i <= m; ++i) cnt[i] += cnt[i - 1];
	for (int i = 0; i < (1 << n); ++i) sa[cnt[rk[i]] --] = i;
	
	for (int k = 0; k < n; ++k) {
		int tot = 0;
		for (int i = 1; i <= (1 << n); ++i) x[tot ++] = sa[i] ^ (1 << k);
		for (int i = 0; i <= m; ++i) cnt[i] = 0;
		for (int i = 0; i < (1 << n); ++i) cnt[rk[i]] ++;
		for (int i = 0; i <= m; ++i) cnt[i] += cnt[i - 1];
		for (int i = tot - 1; i >= 0; --i) sa[cnt[rk[x[i]]] --] = x[i];
		tot = 1;
		swap(rk , x);
		rk[sa[1]] = 1;
		for (int i = 2; i <= (1 << n); ++i) {
			if(x[sa[i]] == x[sa[i - 1]] && x[sa[i] ^ (1 << k)] == x[sa[i - 1] ^ (1 << k)]) rk[sa[i]] = tot;
			else rk[sa[i]] = ++tot;
		}
		if(tot >= (1 << n)) break;
		m = tot + 1; 
	}
	
	for (int i = 0; i < (1 << n); ++i) putchar(s[sa[1] ^ i]);
	
	return 0;
}

CF1654G Snowy Mountain

说实话,带点仙气。

首先是常规分析,需要先算出每个点到基地的最短距离,记为 \(h_x\)

然后发现一个点最少都能走 \(h_x\) 步,最高也只能走 \(2h_x\) 步。

所以路径长度取决于我们能消耗多少势能,即够走多少平层。

容易想到,我们会在一个平层反复横跳以消耗掉富余势能。考虑加紧限制。

发现等价于我们只用在两个点上反复横跳即可达成目标,即我们要走到高度最低的一个平台去反复横跳,然后再一次性滚到基地。

得到了 \(O(n^2)\) 的做法。

接着便是神谕,考虑平台上的点 \(h_x\) 的和。

我们把平台的边断开,形成若干个连通块,然后把这些联通块拼回一号联通块,每次拼会增加一条边,和增加 \(2h_x\) ,且 \(h_x < siz\)\(siz\) 指新加连通块大小。

所以 \(h_x\) 的和是 \(O(n)\) 的。

因此 \(h_x\) 的种类数 \(O(\sqrt{n})\)

对一种种类的平台,我们可以做一个 \(O(n)\)\(dp\) 就可以了。

#include <map>
#include <set>
#include <queue>
#include <cmath>
#include <bitset>
#include <cstdio>
#include <cassert>
#include <cstring>
#include <iostream>
#include <algorithm>
#define pii pair <int , int>
#define pll pair <LL , LL>
#define mp make_pair
#define fs first
#define sc second
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;

//const int Mxdt=100000; 
//static char buf[Mxdt],*p1=buf,*p2=buf;
//#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++;

template <typename T>
void read(T &x) {
	T f=1;x=0;char s=getchar();
	while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
	while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s-'0');s=getchar();}
	x *= f;
}

template <typename T>
void write(T x , char s='\n') {
	if(!x) {putchar('0');putchar(s);return;}
	if(x<0) {putchar('-');x=-x;}
	T tmp[25]={},t=0;
	while(x) tmp[t++]=x%10,x/=10;
	while(t-->0) putchar(tmp[t]+'0');
	putchar(s); 
}

const int MAXN = 2e5 + 5;

int head[MAXN] , to[MAXN << 1] , nxt[MAXN << 1] , cnt;
void add(int u , int v) {nxt[++cnt] = head[u];head[u] = cnt;to[cnt] = v;}
int col[MAXN] , n , h[MAXN];

vector <int> P[MAXN];
int dp[MAXN] , vis[MAXN] , chk[MAXN] , ans[MAXN];
void dfs1(int x , int fa) {
	vis[x] = 1;
	for (int i = head[x]; i; i = nxt[i]) {
		int v = to[i];
		if(v == fa || h[x] != h[v]) continue;
		dp[v] = max(dp[v] , dp[x] - 1);
		dfs1(v , x);
		dp[x] = max(dp[x] , dp[v] - 1);
	}
}

void dfs(int x , int fa) {
	for (int i = head[x]; i; i = nxt[i]) {
		int v = to[i];
		if(v == fa) continue;
		h[v] = min(h[v] , h[x] + 1);
		dfs(v , x);
		h[x] = min(h[x] , h[v] + 1);
	}
}


int main() {
	read(n);
	for (int i = 1; i <= n; ++i) read(col[i]) , h[i] = ((int)1e9) * (!col[i]);
	for (int i = 1; i < n; ++i) {
		int u , v;
		read(u),read(v);
		add(u , v) , add(v , u);
	}
	
	dfs(1 , 0),dfs(1 , 0);
	
	for (int i = 1; i <= n; ++i) P[h[i]].push_back(i);
	
	vector <int> now;
	
	for (int x = 1; x <= n; ++x) {
		ans[x] = h[x];
		for (int i = head[x]; i; i = nxt[i]) {
			int v = to[i];
			chk[x] |= (h[x] == h[v]);
		}
		if(chk[x] && !vis[h[x]]) now.push_back(h[x]) , vis[h[x]] = 1;
	}
	
	sort(now.begin() , now.end());
	for (auto p:now) {
		for (int i = 1; i <= n; ++i) 
			dp[i] = (h[i] != p || !chk[i]) * (-1e9) , vis[i] = 0;
		for (int w = p; w <= n; ++w) {
			for (auto x:P[w]) {
				if(!vis[x]) dfs1(x , 0),dfs1(x , 0);
			}
			for (auto x:P[w]) {
				for (int i = head[x]; i; i = nxt[i]) {
					int v = to[i];
					if(h[v] <= h[x]) continue;
					dp[v] = max(dp[v] , dp[x] + 1);
					dp[v] = min(dp[v] , 0);
				}
			}
		}
		for (int i = 1; i <= n; ++i) if(!dp[i]) ans[i] = min(ans[i] , p); 
	}
	
	for (int i = 1; i <= n; ++i) write(h[i] * 2 - ans[i] , ' ');
	return 0;
}
posted @ 2022-03-30 23:00  Reanap  阅读(55)  评论(0编辑  收藏  举报