1. 程式人生 > 其它 >8.24 校內模擬賽 題解報告

8.24 校內模擬賽 題解報告

沒有摘要的摘要 目錄

8.24 校內模擬賽 題解報告

T1 板子題 板子不會寫 掛

T2 數論結論題 資料鍋了 掛

T3 換根 dp 題 換根寫掛 掛

大概這場考試就是這樣...


關於考試過程以及一些題外話

開考之後花了二十分鐘把三個題的題目看了一下

T1 給了七十分的暴力 嗯 先放著...

很顯然的 AC 自動機的模板題... 但是 AC 自動機感覺寫不出來了...

大概率會調很久... 然後跑路了

T2 一開始看的時候有點懵 分析了一下感覺好像可做 先放著 溜去看 T3

讀完題沒看出什麼東西來 看了下資料 送了四十的暴力 分析了一下 要麼點分治 要麼就 dp

然後跑去把 T1 的七十分拿了

然後跑去猜了一個 T2 的結論 稍微嘗試證了一下 感覺問題不大 碼完自己卡了一下 好像可行的亞子 複雜度好像也沒有問題 有希望能過(狂喜 雖然最後並沒過

然後跑去死磕 T3

換根 dp 寫出來調不出來系列...

調了大半個小時 看還剩下半個小時 順手把四十的暴力寫了 最後也沒調出來


賽後看那 SB 一樣的說了等於沒說的題解 = =

還是待自力更生...

Ariel 說 T1 可以字串雜湊水過去...

麻了 沒想到用 map BS 菜死了菜死了菜死了

但是 Ariel 雜湊沒取模 於是他爆了 呵呵呵呵

麻了 T2 怎麼掛了...

前面三個點怎麼過的?

艹艹艹

跑去看標程 嗯? 結論是對的...

所以為什麼掛了 這絕對不是我的問題 絕對是資料問題(殘念 中午扒著資料看了好久 確實是資料問題

果然 T3 只過了暴力分 調不出來的 dp 沒什麼指望... 後來發現 dp 的初始化和子樹的 siz 鍋了

得分情況

70 + 30 + 30 = 130

T2 很氣

出題人資料鍋了 連帶著 std 跑出來的答案也鍋了

什麼鬼題不開 long long 可以過 開了就 WA 啊 煩

這個 T3 硬是 T 了也沒辦法


題解

由於此次的考試題解過於不可言述 有和沒有一個樣 基本什麼都沒給 所以一下的思路及程式碼均來自個人或機房中大佬的想法

T1 monitor

Ariel: 字串雜湊加 map 直接水過去

然後 BS 就被卡了...

注意模數不要開 \(10^9 + 7\) 改成 \(10^9 + 9\) 就過了


程式碼

程式碼比較冗雜 裡面並不只有正解 還包括了所有部分分的程式碼

考試的時候看一部分寫一部分 想到哪寫到哪了

/*
  Source: 監聽
*/
#include<map>
#include<cstdio>
#include<cstring>
#define int long long
#define pt putchar(' ')
#define pn putchar('\n')
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int base = 137;
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 9;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
	freopen("monitor.in", "r", stdin);
	freopen("monitor.out", "w", stdout);
}
/*----------------------------------------------------------*/
int L, n, m, H[B], h[A], p[B];
char s1[B], s2[A][30];
std::map <int, bool> mp;
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void work1() {
	for(int i = 1; i ^ L + 1; i++)
		for(int j = 1; j ^ n + 1; j++)
		{
			int l = i, k = 1;
			for(; k ^ m + 1; k++, l++)
				if(s1[l] != s2[j][k]) break;
			if(k == m + 1) {Print(i); return ;}
		}
	puts("no");
}
void work2() {
	int res = 0; p[0] = 1;
	for(int i = 1; i ^ Max(L, m) + 1; i++) p[i] = p[i - 1] * base % mod;
	for(int i = 1; i ^ m + 1; i++) res = (res + (s2[1][i] - 96) * p[i - 1] % mod) % mod;
	for(int i = 1; i ^ L + 1; i++)
	{
		H[i] = (H[i - 1] + (s1[i] - 96) * p[i - 1] % mod) % mod;
		if(res * p[i - m] % mod == ((H[i] - H[i - m]) % mod + mod) % mod) {Print(i - m + 1); return ;}
	}
	puts("no");
}
void work3() {
	p[0] = 1;
	for(int i = 1; i ^ Max(L, m) + 1; i++) p[i] = p[i - 1] * base % mod;
	for(int i = 1; i ^ n + 1; i++) for(int j = 1; j ^ m + 1; j++)
		h[i] = (h[i] + (s2[i][j] - 96) * p[j - 1] % mod) % mod;
	for(int i = 1; i ^ L + 1; i++)
	{
		H[i] = (H[i - 1] + (s1[i] - 96) * p[i - 1] % mod) % mod;
		if(i >= m) for(int j = 1; j ^ n + 1; j++)
			if(((H[i] - H[i - m]) % mod + mod) % mod == h[j] * p[i - m] % mod) {Print(i - m + 1); return ;}
	}
	puts("no");
}
void work4() {
	p[0] = 1;
	for(int i = 1; i ^ Max(L, m) + 1; i++) p[i] = p[i - 1] * base % mod;
	for(int i = 1; i ^ n + 1; i++)
	{
		int res = 0;
		for(int j = 1; j ^ m + 1; j++) res = (res + (s2[i][j] - 96) * p[j - 1] % mod) % mod;
		mp[res] = 1;
	}
	for(int i = 1; i ^ L + 1; i++)
	{
		int res = 0;
		for(int j = 0; j ^ m; j++) res = (res + (s1[i + j] - 96) * p[j] % mod) % mod;
		if(mp[res]) {Print(i); return ;}
	}
	puts("no");
}
void Main() {
//	File();
	L = read(); n = read(); m = read();
	for(int i = 1; i ^ n + 1; i++) scanf("%s", s2[i] + 1);
	scanf("%s", s1 + 1);
	if(L * n * m <= D) work1();
	else if(n == 1) work2();
	else if(L * n <= D) work3();
	else work4();
//	int x = read();
//	if(x == 1) work1();
//	if(x == 2) work2();
//	if(x == 3) work3();
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}
/*
11 3 3
aba
cba
abc
aaabbabzabz
*/

T2 lab

對造資料的人提出譴責

原題的資料中有給出 \(T_i \leq 10^9\) 的範圍的 但是第三, 四, 五個點的資料中依然出現了超過三十二位整型的數 而標程讀入的 \(T\)\(int\) 型別 所以連帶著輸出答案都不對

所以 BS 的才是對的


這題一開始的時候是看的第一部分的暴力分 然後分析了一下 猜了個結論 算了一下複雜度...

好像能過...(笑

然後就寫了

最後並沒有過(悲


可以說是算是一個結論題了

先說做法

先對原圖把樹建出來 對於這個時候的詢問 兩點的距離是唯一的 可以直接算 然後當有加邊的時候 將加入的一條邊與原來的樹邊形成的一個環的長度記下來 維護環的長度的 \(\gcd\) 即為 \(g\) 每加入一條邊的時候進行維護 對於詢問 如果兩點之間的距離與給出的 \(t\) 在模 \(g\) 的意義下同餘 則為 \(yes\) 否則為 \(no\)

Q: 為什麼

A:

題中給出對於每條邊 正著走和反著走的代價是相反的 所以對於一條路徑 來回走一遍甚至若干遍對答案是沒有影響的 所以

隨著加邊圖中會不斷構成環 不難設想可以通過一條路徑到達一個環 然後可以再環上轉任意多圈再回來 經過的那一條路徑時不會對答案做貢獻的

所以有用的就只有那個環 而這個環是可以正著走也可以反著走的

設兩點之間的樹上距離為 \(dis\) 環長為 \(len\) 的話 那麼在只有一個環的時候 \(u\)\(v\) 的路徑長度就是 \(dis_{u, v} + k \times len\)

顯然這個結論可以推廣到若干個環 不妨設為 \(m\) 個 這樣就可以得到兩點之間的路徑:

\[Dis_{u, v} = dis_{u, v} + \sum_{i = 1}^mk_i \times len_i \]

對於所有的 \(k\) 其取值都為任意整數 那麼可以找到一個 \(K\) 使得上式為:

\[Dis_{u, v} = dis_{u, v} + K \times \gcd_{i = 1}^m(len_i) \]

BS: \(\gcd(a, b) = \gcd(a - b, b)\)

Ariel: 裴蜀定理

BS: ...

BS 太菜了 不知道裴蜀定理真是抱歉

回到正題

\(g = \gcd_{i = 1}^m(len_i)\) 題目相當於要求我們判斷 是否有:

\[dis_{u, v} + K \times g = t \]

那個 \(K\) 是不確定的 但是這個式子的形式很容易啟發我們將其轉化為同餘式 即:

\[dis_{u, v} \equiv t \pmod g \]

於是就有了上面的那個做法


一些題外話

由於這個題的資料鍋掉了 所以讀入 \(T\) 的時候需要讀入為 int 而其他位置需要用 long long 具體見程式碼

T2 在評測的時候 zxsoul 以其優秀的搜尋技巧 卡住評測姬數分鐘

成功耽誤老呂吃飯

我願稱你為最強

modify(u, v, w); modify(u, v, -w);

神仙死迴圈 這你的 dfs 能出來就有鬼了 = =


程式碼

/*
  Source: 實驗室
*/
#include<cstdio>
#include<cstring>
#define ll long long
#define pt putchar(' ')
#define pn putchar('\n')
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int A = 1e5 + 7;
const int B = 5e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
	freopen("lab4.in", "r", stdin);
	freopen("lab.out", "w", stdout);
}
/*----------------------------------------------------------*/
ll n, Q, dis[A], flag, g;
struct Query {ll opt, u, v, t;} q[B];
struct edge {ll v, w, nxt;} e[B << 1];
ll head[A], ecnt;
/*----------------------------------------------------------*/
inline ll read() {
	ll x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(ll x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void add_edge(int u, int v, ll w) {e[++ecnt] = (edge){v, w, head[u]}; head[u] = ecnt;}
namespace Cut {
	int dep[A], siz[A], top[A], fa[A], son[A], tcnt;
	void dfs1(int u, int pre) {
		siz[u] = 1; fa[u] = pre; dep[u] = dep[pre] + 1;
		for(int i = head[u]; i; i = e[i].nxt)
		{
			ll v = e[i].v, w = e[i].w; if(v == pre) continue;
			dis[v] = dis[u] + w; dfs1(v, u); siz[u] += siz[v];
			if(!son[u] || siz[son[u]] < siz[v]) son[u] = v;
		}
	}
	void dfs2(int u, int tp) {
		top[u] = tp; if(!son[u]) return ; dfs2(son[u], tp);
		for(int i = head[u]; i; i = e[i].nxt)
		{
			ll v = e[i].v; if(v == fa[u] || v == son[u]) continue;
			dfs2(v, v);
		}
	}
	int LCA(int x, int y) {
		while(top[x] != top[y])
		{
			if(dep[top[x]] < dep[top[y]]) Swap(x, y);
			x = fa[top[x]];
		}
		return dep[x] < dep[y] ? x : y;
	}
}
ll gcd(ll x, ll y) {return y ? gcd(y, x % y) : x;}

void Main() {
	File();
	n = read(); Q = read();
	for(ll i = 1, x, y, z; i ^ n; i++) x = read(), y = read(), z = read(), add_edge(x, y, z), add_edge(y, x, -z);
	Cut::dfs1(1, 0); Cut::dfs2(1, 1);
	for(int i = 1; i ^ Q + 1; i++)
	{
		ll opt = read(), x = read(), y = read(), lca = Cut::LCA(x, y); int t = read();
		ll Dis = (-(dis[x] - dis[lca]) + (dis[y] - dis[lca]) - t);
		if(opt)
		{
			if(!g) {if(Dis) puts("no"); else puts("yes");}
			else
			{
				Dis = (Dis % g + g) % g;
				if(!Dis) puts("yes"); else puts("no");
			}
		}
		else g = gcd(g, Dis);
	}
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}

題目已上傳到洛谷 資料已經修復

是用 BS 的程式碼跑的 驕傲

將上面的那個 \(int\) 改成 \(ll\) 就好了


T3 civilization

換根 dp

方程和轉移都比較顯然

初始:

\(f_{u, 0/1}\) 表示以 \(u\) 為根的子樹中 到 \(u\) 點距離為 偶數/奇數 的點的距離和

轉移考慮 \(w_{u, v}\) 的奇偶性 同時需要維護到 \(u\) 點距離為 偶數/奇數 的點的數量

換根:

\(f_{u, v}\) 表示到 \(u\) 點距離為 偶數/奇數 的點的距離和

同樣轉移考慮 \(w_{u, v}\) 的奇偶性 此時仍然需要動態的維護全樹中到當前根節點距離為 偶數/奇數 的點的個數 BS 忘記這茬爆零了

具體細節見程式碼 程式碼中 BS 是通過相乘進行分類討論的


程式碼

/*
  Source: 文明
*/
#include<cstdio>
#include<cstring>
#define int long long
#define pt putchar(' ')
#define pn putchar('\n')
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
	freopen("civilization.in", "r", stdin);
	freopen("civilization.out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, q, siz0[B], siz1[B], size0[B], size1[B], f[B][2], du[B];
int dis[2021][2021], ans[2021][2];
struct edge {int v, w, nxt;} e[B << 1];
int head[B], ecnt;
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void add_edge(int u, int v, int w) {e[++ecnt] = (edge){v, w, head[u]}; head[u] = ecnt;}
void dfs(int rt, int u, int pre) {
	for(int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].v, w = e[i].w;
		if(v == pre) continue;
		dis[rt][v] = dis[rt][u] + w;
		if(dis[rt][v] & 1) ans[rt][1] += dis[rt][v];
		else ans[rt][0] += dis[rt][v];
		dfs(rt, v, u);
	}
} 
void work1() {
	for(int i = 1; i ^ n + 1; i++) dfs(i, i, 0);
	for(int i = 1; i ^ q + 1; i++)
	{
		int x = read();
		Print(ans[x][1]); pt; Print(ans[x][0]); pn;
	}
}
void dfs1(int u, int pre) {
	siz0[u] = 1;
	for(int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].v, w = e[i].w;
		if(v == pre) continue;
		dfs1(v, u);
		siz0[u] += siz0[v] * (w & 1 ^ 1) + siz1[v] * (w & 1);
		siz1[u] += siz0[v] * (w & 1) + siz1[v] * (w & 1 ^ 1);
		f[u][0] += (f[v][0] + siz0[v] * w) * (w & 1 ^ 1) + (f[v][1] + siz1[v] * w) * (w & 1);
		f[u][1] += (f[v][0] + siz0[v] * w) * (w & 1) + (f[v][1] + siz1[v] * w) * (w & 1 ^ 1);
		
	}
}
void dfs2(int u, int pre) {
	for(int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].v, w = e[i].w;
		if(v == pre) continue;
		size0[v] = size0[u] * (w & 1 ^ 1) + size1[u] * (w & 1);
		size1[v] = size0[u] * (w & 1) + size1[u] * (w & 1 ^ 1);
		f[v][0] = (f[u][1] - siz0[v] * w + (size1[u] - siz0[v]) * w) * (w & 1) + (f[u][0] - siz0[v] * w + (size0[u] - siz0[v]) * w) * (w & 1 ^ 1);
		f[v][1] = (f[u][1] - siz1[v] * w + (size1[u] - siz1[v]) * w) * (w & 1 ^ 1) + (f[u][0] - siz1[v] * w + (size0[u] - siz1[v]) * w) * (w & 1);
		dfs2(v, u);
	}
}
void work2() {
	dfs1(1, 0); size0[1] = siz0[1]; size1[1] = siz1[1]; dfs2(1, 0);
	for(int i = 1; i ^ q + 1; i++)
	{
		int x = read();
		Print(f[x][1]); pt; Print(f[x][0]); pn;
	}
}
void Main() {
//	File();
	n = read(); q = read();
	for(int i = 1, x, y, z; i ^ n; i++)
		x = read(), y = read(), z = read(), add_edge(x, y, z), add_edge(y, x, z);
	work2();
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}