1. 程式人生 > 實用技巧 >20201115gryz模擬賽解題報告

20201115gryz模擬賽解題報告

寫在前面

T1:期望100pts,實際0pts(7:50 ~ 8:50

T2:期望0pts,實際0pts(10:00 ~ 10:35

T3:期望20pts,實際40pts( 9:10 ~ 10:00,10:35 ~ 11:00

排名:rank2/10

感覺今天狀態好多了~ >-<!

今天修樹剖太累了,先咕咕咕,有空再把剩下兩道題補上還有T3的滿分程式碼,嘻嘻

解題過程

有了CSP的經驗,先通讀一遍題目,果斷開T1,發現數的位數比較小,感覺暴力可以過,但是全排列不會寫,發現貪心好像更簡單,胡亂搞了兩下,過了樣例,判了前導零

感覺沒問題了,去吃了四樓頓飯,接著開T2,手模樣例 + 亂搞,以為是個找規律,好像並不會做,暴力也不會打,

決定先打T3暴力,讀過一遍題目,發現這就是個裸樹剖,算著暴力會T,為了騙分,暴力搞上再說,此時剛好打9:30下課鈴,9:50,上課鈴響,稍微一調,過了樣例

(我直接感動哭了,樹剖從來沒有打這麼快過,還沒有寫掛)

繼續手模T2,沒整明白,後來想T3優化也不成,最後五分鐘發現T1忘記判負了,懶得改了~

(後來發現T1沒負數,我貪心貪錯了

正解

T1

題面

Solution

暴力列舉全排列,複雜度最多 \(10 \times 9!\) , 輕鬆A掉

Talk is cheap,show me the code:

next_permutation函式不太會用啊,調了一下午樹剖,太累了,懶得寫了

我的貪心(不忍直視

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>

using namespace std;

int T, k;
string s1, s2;

int read(){
	int s = 0, w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0', ch = getchar();
	return s * w;
}

int main(){
	//freopen("cooperate.in","r",stdin);
	//freopen("cooperate.out","w",stdout);
	T = read();
	while(T--){
		cin>>s1; k = read();
		s2 = s1;
		int len = s1.size(), wz, k2 = k;
		//最大值
		for(int i = 0; i < len; ++i){
			wz = i;
			for(int j = i + 1; j < len; ++j){
				if(s1[i] < s1[j] && s1[wz] < s1[j]){
					wz = j;
				}
			}
			if(wz != i && k){
				int x = s1[i];
				s1[i] = s1[wz]; s1[wz] = x;
				k--;
			}
		}
		//最小值
		wz = 0;
		for(int j = 1; j < len; ++j){//最小值需要考慮不能有前導零,只要保證第一位不是就好了
			if(s2[0] > s2[j] && s2[wz] >= s2[j] && s2[j] != '0'){
				wz = j;
			}
		}
		if(wz != 0 && k2){
			int x = s2[0];
			s2[0] = s2[wz], s2[wz] = x;
			k2--;
		}
		
		for(int i = 1; i < len; ++i){
			wz = i;
			for(int j = i + 1; j < len; ++j){
				if(s2[i] > s2[j] && s2[wz] >= s2[j]){
					wz = j;
				}
			}
			if(wz != i && k2){

				int x = s2[i];
				s2[i] = s2[wz], s2[wz] = x;
				k2--;
			}
		}
		int sum1 = 0, sum2 = 0;
		for(int i = 0; i < len; ++i){
			sum1 = (sum1 << 1) + (sum1 << 3) + s1[i] - '0';
			sum2 = (sum2 << 1) + (sum2 << 3) + s2[i] - '0';
		}
		// cout<<sum1<<endl<<sum2<<endl;
		printf("%d\n", sum1 - sum2);
	}
	return 0;
}

/*
8
12 1
213 2
998244350 1
998244350 2
998244353 1
998244353 2
998244353 3
998244353 300
 */

T2

題面

Solution

把整個區間分成 \(k (或者 k + 1)\) 份,發現翻轉兩次就能使兩盞處在同一位置的燈都點亮,

然後……然後還沒看呢

T3

題面

solution

這好像是今天唯一的成果

裸的樹剖,在求和是注意優化, \(n ^ 2\) 暴力只有20pts

sbw給出了優化方案(O(n)遍歷所有邊求和,70pts:

\[ans_x = \sum_{j \in son_x} w_j * (siz_x - siz_j) * siz_j \bmod 2019 \]

Talk is cheap,show me the code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>

using namespace std;
const int MAXN = 5e4+5;
const int mod = 2019;

int n, Q, cnt;
int dfn[MAXN], pre[MAXN], fath[MAXN], son[MAXN], dep[MAXN], top[MAXN], siz[MAXN], a[MAXN], rd[MAXN];

int read(){
	int s = 0, w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0', ch = getchar();
	return s * w;
}

namespace Seg{
	#define lson i << 1
	#define rson i << 1 | 1
	struct Tree{
		int sum, lazy, len;
	}tree[MAXN << 2];
	void push_up(int i){
		tree[i].sum = (tree[lson].sum + tree[rson].sum) % mod;
	}
	void push_down(int i){
		if(tree[i].lazy){
			tree[lson].lazy = (tree[lson].lazy + tree[i].lazy) % mod;
			tree[rson].lazy = (tree[rson].lazy + tree[i].lazy) % mod;
			tree[lson].sum = (tree[lson].sum + tree[i].lazy * tree[lson].len) % mod;
			tree[rson].sum = (tree[rson].sum + tree[i].lazy * tree[rson].len) % mod;
			tree[i].lazy = 0;
		}
	}
	void build(int i, int l, int r){
		tree[i].lazy = 0, tree[i].len = r - l + 1;
		if(l == r){
			tree[i].sum = a[pre[l]] % mod;
			return ;
		}
		int mid = (l + r) >> 1;
		build(lson, l, mid), build(rson, mid + 1, r);
		push_up(i);
		return ;
	}
	void add(int i, int l, int r, int L, int R, int k){
		if(L <= l && r <= R){
			tree[i].sum = (tree[i].sum + tree[i].len * k) % mod;
			tree[i].lazy = (tree[i].lazy + k) % mod;
			return ;
		}
		push_down(i);
		int mid = (l + r) >> 1;
		if(mid >= L) add(lson, l, mid, L, R, k);
		if(mid < R) add(rson, mid + 1, r, L, R, k);
		push_up(i);
		return ;
	}
	int get_sum(int i, int l, int r, int L, int R){
		if(L <= l && r <= R){
			return tree[i].sum % mod;
		}
		push_down(i);
		int mid = (l + r) >> 1, ans = 0;
		if(mid >= L) ans = (ans + get_sum(lson, l, mid, L, R)) % mod;
		if(mid < R) ans = (ans + get_sum(rson, mid + 1, r, L, R)) % mod;
		return ans % mod;
	}
}

namespace Cut{
	struct edge{
		int to, w, nxt;
	}e[MAXN << 1];
	int head[MAXN], num_edge = 0;
	void add_edge(int from, int to, int w){
		e[++num_edge] = (edge){to, w, head[from]}, head[from] = num_edge;
	}
	void dfs(int x, int fa){
		siz[x] = 1, dep[x] = dep[fa] + 1, fath[x] = fa;
		for(int i = head[x]; i; i = e[i].nxt){
			int v = e[i].to;
			if(v == fa) continue;
			dfs(v, x);
			siz[x] += siz[v];
			if(siz[v] > siz[son[x]]) son[x] = v; 
		}
	}
	void dfs2(int x, int tp){
		dfn[x] = ++cnt, pre[cnt] = x, top[x] = tp;
		if(son[x]) dfs2(son[x], tp);
		for(int i = head[x]; i; i = e[i].nxt){
			int v = e[i].to;
			if(v == fath[x] || v == son[x]) continue;
			dfs2(v, v);
		}
	}
	void change(int x, int y, int k){
		while(top[x] != top[y]){
			if(dep[top[x]] < dep[top[y]]) swap(x, y);
			Seg::add(1, 1, n, dfn[top[x]], dfn[x], k);
			x = fath[top[x]];
		}
		if(dfn[x] > dfn[y]) swap(x ,y);
		Seg::add(1, 1, n, dfn[x] + 1, dfn[y], k);
		return ;
	}
	int get(int x, int y){
		int ans = 0;
		while(top[x] != top[y]){
			if(dep[top[x]] < dep[top[y]]) swap(x, y);
			ans = (ans + Seg::get_sum(1, 1, n, dfn[top[x]], dfn[x])) % mod;
			x = fath[top[x]];
		}
		if(dfn[x] > dfn[y]) swap(x, y);
		ans = (ans + Seg::get_sum(1, 1, n, dfn[x] + 1, dfn[y])) % mod;
		return ans % mod;
	}
	int get_s(int x, int rt){//O(n)遍歷所有邊求和,正解,100pts
		int ans = 0;
		for(int i = head[x]; i; i = e[i].nxt){
			int v = e[i].to;
			if(v == fath[x]) continue;
			ans = (ans + Seg::get_sum(1, 1, n, dfn[v], dfn[v]) % mod * (siz[rt] - siz[v]) % mod * siz[v]) % mod;
			ans = (ans + get_s(v, rt)) % mod;
		}
		return ans;
	}
	int get_ss(int x){//暴力,列舉所有情況求和,只有40pts
		int ans = 0;
		for(int i = dfn[x]; i < dfn[x] + siz[x] - 1; ++i){
			for(int j = i + 1; j <= dfn[x] + siz[x] - 1; ++j){
				ans = (ans + get(pre[i], pre[j])) % mod;
			}
		}
		return ans;
	}
}

int main(){
	//freopen("network.in","r",stdin);
	//freopen("network.out","w",stdout);
	n = read(), Q = read();
	for(int i = 2, u; i <= n; ++i){
		u = read(), a[i] = read();
		Cut::add_edge(u, i, a[i]), Cut::add_edge(i, u, a[i]);
		rd[i]++;
	}
	
	int wz;
	for(int i = 1; i <= n; ++i) if(!rd[i]) {wz = i; break; }
	
	Cut::dfs(wz, 0), Cut::dfs2(wz, wz), Seg::build(1, 1, n);
	
	for(int i = 1, x, y, k; i <= Q; ++i){
		string s;
		cin>>s;
		if(s[0] == 'I'){
			x = read(), y = read(), k = read();
			Cut::change(x, y, k);
		}
		if(s[0] == 'A'){
			x = read();
			// printf("%d\n", Cut::get_ss(x) % mod);
			printf("%d\n", Cut::get_s(x, x) % mod);
		}
	}
	return 0;
}
/*
5 5
1 1
2 5
1 2
2 1
INC 2 4 2
INC 3 4 1
ASK 2
INC 2 5 3
ASK 1
 */