CF # 1458 简要题解

A

模拟

B

D P \mathcal{DP} DP

C

我们将一个格子看成三元组 ( i , j , a i , j ) (i,j,a_{i,j}) (i,j,ai,j)
那么 I I I 就相当于是将所有 ( x , y , z ) (x,y,z) (x,y,z) 变成 ( x , z , y ) (x,z,y) (x,z,y)
我们维护一下三维的置换,对平移打上标记就可以了

#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
cs int N = 1e3 + 50, M = 1e5 + 50;
int T, n, m, a[N][N], b[N][N];
int s[3], c[3]; char str[M];
void Main(){
	cin >> n >> m;
	for(int i = 0; i < n; i++)
	for(int j = 0; j < n; j++)
	scanf("%d", &a[i][j]), --a[i][j];
	for(int i = 0; i < 3; i++)
	s[i] = 0, c[i] = i;
	scanf("%s", str);
	for(int i = 0; i < m; i++){
		if(str[i] == 'L') --s[1];
		if(str[i] == 'R') ++s[1];
		if(str[i] == 'U') --s[0];
		if(str[i] == 'D') ++s[0];
		if(str[i] == 'I') swap(c[1], c[2]), swap(s[1], s[2]);
		if(str[i] == 'C') swap(c[0], c[2]), swap(s[0], s[2]);
	}
	for(int i = 0; i < n; i++)
	for(int j = 0; j < n; j++){
		int t[3]; t[0] = i, t[1] = j, t[2] = a[i][j];
		int x = t[c[0]], y = t[c[1]], z = t[c[2]];
		x = ((x + s[0]) % n + n) % n;
		y = ((y + s[1]) % n + n) % n;
		z = ((z + s[2]) % n + n) % n; b[x][y] = z; 
	}
	for(int i = 0; i < n; i++, puts(""))
	for(int j = 0; j < n; j++)
	cout << b[i][j] + 1 << " "; puts("");
}
int main(){
	#ifdef FSYo
	freopen("1.in", "r", stdin);
	#endif
	cin >> T; 
	while(T--) Main();
	return 0;
}

D

考虑将 0 0 0 看成 ( x , x + 1 ) (x,x+1) (x,x+1) 连边, 1 1 1 看成 ( x , x − 1 ) (x,x-1) (x,x1) 连边
注意到反转一个子串相当于是将一条欧拉回路倒过来走
题目要求我们尽量向右走,我们每一步维护一下能不能向右走就可以了

#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
cs int N = 1e6 + 50;
int T, n; char S[N];
int a[N], b[N];
void Main(){
	scanf("%s", S + 1);
	n = strlen(S + 1);
	for(int i = 1, s = n; i <= n; i++){
		if(S[i] == '0') ++a[s], ++s; 
		if(S[i] == '1') ++b[s], --s;
	} int t = n; 
	while(true){
		bool ok = a[t] && (b[t + 1] || !b[t]);
		if(ok) putchar('0'), --a[t], ++t;
		else if(b[t]) putchar('1'), --b[t], --t;
		else break;
	} puts("");
}
int main(){
	#ifdef FSYo
	freopen("1.in", "r", stdin);
	#endif
	cin >> T;
	while(T--) Main();
	return 0;
} 

E

将原问题放到二维平面上
不妨求出 f x , y ∈ { 0 , 1 } f_{x,y}\in\{0,1\} fx,y{0,1} 表示每个点是必败还是必胜
那么我们知道若 ( x , y ) (x,y) (x,y) 左边和下面都为 1 1 1 那么它为 0 0 0
同时 ( x + δ , y + δ ) (x+\delta,y+\delta) (x+δ,y+δ) 为可能为 1 1 1 的点,每行每列只可能有一个点为 1 1 1
于是我们设计一个算法:若当前为关键点,那么移动到 ( x + 1 , y + 1 ) (x+1,y+1) (x+1,y+1)
若存在 ( x , y ′ < y ) (x,y'<y) (x,y<y) 为关键点,那么移动到 ( x + 1 , y ) (x+1,y) (x+1,y)
若存在 ( x ′ < x , y ) (x'<x,y) (x<x,y) 为关键点,那么移动到 ( x , y + 1 ) (x,y+1) (x,y+1)
否则当前 f x , y f_{x,y} fx,y 1 1 1,此时只需要移动到 ( x + 1 , y + 1 ) (x+1,y+1) (x+1,y+1) 就可以了
我们按 x x x 做扫描线,维护 ( x ′ < x , y ) (x'<x,y) (x<x,y) y y y 的集合,复杂度 O ( n log ⁡ n ) \mathcal{O}(n\log n) O(nlogn)

#include<bits/stdc++.h>
#define cs const
#define pb push_back
#define fi first
#define se second
using namespace std;
cs int oo = 1e9 + 7;
typedef pair<int, int> pi;
cs int N = 1e5 + 50;
int n, m, ans[N], y[N];
map<pi, bool> ex; 
map<int, vector<int> > G;
int main(){
	#ifdef FSYo
	freopen("1.in", "r", stdin);
	#endif
	cin >> n >> m; 
	for(int i = 1, x, y; i <= n; i++){
		scanf("%d%d", &x, &y);
		ex[pi(x, y)] = true, G[x].pb(y);
	} ex[pi(0, 0)] = 1, G[0].pb(0);
	for(int i = 1, x, y; i <= m; i++){
		scanf("%d%d", &x, &y);
		if(!ex[pi(x, y)]) G[x].pb(-i), ::y[i] = y; 
	}
	int lp = -1, lm = 0; set<int> Y; 
	for(auto z : G){
		int x = z.fi; vector<int> e = z.se;
		for(int i = lp + 1; i < x; ){
			while(Y.find(lm) != Y.end()) ++lm;
			auto t = Y.lower_bound(lm);
			if(t == Y.end()) { lm += x - i; break; }
			int dt = min(*t - lm, x - i); lm += dt, i += dt;
		} while(Y.find(lm) != Y.end()) ++lm; int mn = oo;
		for(int z : G[x]) if(z >= 0) mn = min(mn, z);
		if(lm < mn) Y.insert(lm), mn = lm;
		for(int z : G[x]) {
			if(z >= 0) Y.insert(z);
			else ans[-z] = y[-z] != mn;
		} lp = x; 
	} for(int i = 1; i <= m; i++)
	cout << (ans[i] ? "WIN" : "LOSE") << '\n';
	return 0;
}

F

分治,考虑求出 ∑ i ∈ [ l , m i d ] ∑ r ∈ [ m i d + 1 , r ] d i a m ( i , j ) \sum_{i\in[l,mid]}\sum_{r\in[mid+1,r]}diam(i,j) i[l,mid]r[mid+1,r]diam(i,j)
我们对左边的每个后缀 i i i 求出 ( u , R ) (u,R) (u,R)
表示用 ( u , R ) (u,R) (u,R) 可以包住集合 [ i , m i d ] [i,mid] [i,mid] 其中 R R R 是极小的
合并两个集合可以通过树上倍增实现
枚举左边的一个 i i i,那么 j j j 可以分成三类,
[ m i d + 1 , j ] ∈ [ i , m i d ] , [ i , m i d ] ∈ [ m i d + 1 , j ] , o t h e r w i s e [mid+1,j]\in[i,mid],[i,mid]\in [mid+1,j],otherwise [mid+1,j][i,mid],[i,mid][mid+1,j],otherwise
可以通过双指针来维护
对于前两类的贡献可以快速算,中间的贡献相当于是 ∑ j r i + r j + d ( u i , u j ) \sum_jr_i+r_j+d(u_i,u_j) jri+rj+d(ui,uj)
用点分树维护就可以了

#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
typedef long long ll;
cs int N = 2e5 + 50;
int n; vector<int> G[N];
void add(int u, int v){ G[u].pb(v), G[v].pb(u); }

int sz[N], Mx, rt, all; bool ban[N]; 
void gsz(int u, int f){
	sz[u] = 1; for(int v : G[u]) 
	if(!ban[v] && v != f)
	gsz(v, u), sz[u] += sz[v];
} void grt(int u, int f){
	int mx = all - sz[u];
	for(int v : G[u]) if(v != f && !ban[v]) 
	grt(v, u), mx = max(mx, sz[v]); 
	if(mx < Mx) Mx = mx, rt = u; 
} int anc[N], _d[N], d[20][N];
void dfs(int u, int f, int r){
	for(int v : G[u]) 
	if(v != f && !ban[v])
	d[r][v] = d[r][u] + 1, dfs(v, u, r);
} void work(int u, int f){
	gsz(u, 0); Mx = all = sz[u]; grt(u, 0); 
	ban[u = rt] = true;
	_d[u] = _d[anc[u] = f] + 1; 
 	dfs(u, 0, _d[u]);
	for(int v : G[u]) if(!ban[v]) work(v, u);
} 
int c[N]; ll b[N], _b[N];
void ins(int x, int vl){
	for(int u = x, t = _d[x]; u; u = anc[u], --t){
		if(t > 1) _b[u] += vl * d[t - 1][x];
		c[u] += vl, b[u] += vl * d[t][x];
	}
} ll ask(int x){
	ll ans = b[x]; 
	for(int u = x, v = anc[x], t = 
	_d[x] - 1; v; u = v, v = anc[v], --t)
	ans += 1ll * (c[v] - c[u]) * d[t][x] + b[v] - _b[u];
	return ans; 
}

int dep[N], fa[N][20];
void pre_dfs(int u, int f){
	dep[u] = dep[fa[u][0] = f] + 1; 
	for(int i = 1; i <= 17; i++) 
	fa[u][i] = fa[fa[u][i - 1]][i - 1];
	for(int v : G[u]) if(v != f) pre_dfs(v, u);
} int LCA(int x, int y){
	if(dep[x] < dep[y]) swap(x, y);
	for(int i = 17; ~i; i--)
	if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
	if(x == y) return x;
	for(int i = 17; ~i; i--)
	if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
	return fa[x][0];
} int dist(int x, int y){ return dep[x] + dep[y] - 2 * dep[LCA(x, y)]; }
int jmp(int x, int d){ 
	for(int i = 17; ~i; i--)
	if(d >= (1 << i)) d -= 1 << i, x = fa[x][i]; return x; 
} int fnd(int x, int y, int d){
	int l = LCA(x, y);
	if(dep[x] - dep[l] >= d) return jmp(x, d);
	d -= dep[x] - dep[l];
	return jmp(y, dep[y] - dep[l] - d);
}
struct lll{ 
	int u, r;
	lll operator + (cs lll &z) {
		int d = dist(u, z.u);
		if(r + d <= z.r) return z; 
		if(z.r + d <= r) return *this;
		int t = (r + z.r + d) / 2; 
		int p = fnd(u, z.u, t - r);
		return (lll){p, t};
	} bool in(cs lll &z){ return z.r + dist(u, z.u) <= r; }
	bool _in(cs lll &z){ return z.r + dist(u, z.u) < r; }
} p[N]; ll Ans;
void cope(int l, int r){
	if(l == r) return;
	int mid = (l + r) >> 1; 
	p[mid] = (lll){mid, 0};
	p[mid + 1] = (lll){mid + 1, 0};
	for(int i = mid - 1; i >= l; i--)
	p[i] = (lll){i, 0} + p[i + 1];
	for(int i = mid + 2; i <= r; i++)
	p[i] = (lll){i, 0} + p[i - 1];
	int lp = mid, rp = mid + 1; 
	static ll s[N]; s[r + 1] = 0; 
	for(int i = r; i > mid; i--)
	s[i] = s[i + 1] + p[i].r; 
	for(int i = mid; i >= l; i--){
		while(rp <= r && !p[rp].in(p[i])) ins(p[rp].u, 1), ++rp;
		while(lp < r && p[i]._in(p[lp + 1])) ++lp, ins(p[lp].u, -1);
		Ans += s[rp] + 1ll * p[i].r * (lp - mid);
		Ans += (s[lp + 1] - s[rp] + 1ll *
		p[i].r * (rp - lp - 1) + ask(p[i].u)) / 2; 
	} while(lp + 1 < rp) ++lp, ins(p[lp].u, -1);
	cope(l, mid), cope(mid + 1, r);
}
int main(){
	#ifdef FSYo
	freopen("1.in", "r", stdin);
	#endif
	cin >> n;
	for(int i = 1, u, v; i < n; i++)
	scanf("%d%d", &u, &v), 
	add(u, i + n), add(i + n, v);
	pre_dfs(1, 0); work(1, 0);
	cope(1, n);
	cout << Ans << '\n';
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值