A
B

[HZOI] CSP-S模拟20 9.11 赛后总结

[HZOI] CSP-S模拟20 9.11 赛后总结

赛时策略:首先看T1,没有特别明确的思路,感觉是树剖分讨,于是直接开写,然后发现分讨不够,持续分讨,一直到开赛 1h50min 左右,此时已经狂暴对着 2e5 大阳历调了至少 10 多个询问,比较输出文件与答案文件,大概只有7、80组过不去,但是红温了,调不下去了。

模拟赛全部爆 0 的回忆在我脑边萦绕,然后果断放弃T1,转头去看 T2 。

T2 貌似没有发现什么特殊的性质,感觉像分块,但是空间开不下,于是写了一个 bitset 优化的暴力,然后写假了。

样例不过,有点恼火,然后直接选择打暴力,然后打了 \(O(n ^ 2)\) 暴力,获得 40 pts。

此时距离结束还有 2h ,但是 T3 ,T4 都还没看,出去上了个厕所冷静一下,心想 T2 暴力至少有 20 呢,于是心情平复下来,回来直接开 T3.

T3 看了一会,想可能是 DP ,然后推了一会儿柿子,发现不怼,这个柿子怎么没有转移,感觉貌似可以贪心。

但是想了一想,发现可以 Hank ,但是不写白不写,于是迅速码了个贪心交了上去。

T4 看不懂,感觉柿子可以推,但是 1h 我应该写不出来,而且还有一个烂尾的 T1 没调呢。

果断回去调 T1 ,爽调 45 min,离结束只有 10 min 的时候,大样例过了,但是因为没想到倍增,导致这个 300 多行的大分讨大样例跑了 1.9s 。

有些崩溃,但是不交白不交,直接交上。

结束前算了一下,自己的期望得分应该是: (15 + 20 + 0 + 0 = 35)

感觉有点难受,毕竟前前后后调了近 3h 的 T1 时间复杂度不对,略有伤心,而且没有想出优化,感觉自己怎么这么 fw,emo地等待比赛结束。

惊喜: T1 没慢太多,TLE 35 pts,T2 的常数太小 && 数据太水 40 pts,T3 假贪心没过 Hank,但是过了其它所有点 96 pts。// 唐完了

赛后得分: (35 + 40 + 96 + 0 = 171)。

感觉太惊喜了,这场模拟赛前前后后跟做过山车一样,感觉好像整整经历了一遍自己的 OI 生涯(还没结束就是了)。

题解

T1 : 就是一道树上倍增板子题,事实上只要分讨两部分就可以了,一部分是 \(s1\)\(t\) 的距离小于 \(s2\)\(t\) 的距离,另一部分就是小于。然后统计答案直接在树上跑倍增就行了,时间复杂度: \(O(q \log(n))\)

T1 正解
#include<bits/stdc++.h>
#define int long long
#define add(a,b) to[++ tot] = b,nxt[tot] = h[a],h[a] = tot
#define con putchar(' ')
#define ent putchar('\n')
#define Blue_Archive return 0
using namespace std;
const int N = 2e5 + 7;
const int M = 4e5 + 7;

int n;
int m;
int tot;
int ans;
int h[N];
int to[M];
int nxt[M];
int dep[N];
int fa[N][20];

inline int read()
{
	int k = 0,f = 1;
	char c = getchar();
	while(c < '0' || c > '9')
	{
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') k = (k << 3) + (k << 1) + c - '0',c = getchar();
	return k * f;
}

inline void write(int x)
{
	if(x < 0) putchar('-'),x = -x;
	if(x > 9) write(x / 10);
	putchar(x % 10 + '0');
}

inline void dfs(int x,int f)
{
	dep[x] = dep[f] + 1;
	fa[x][0] = f;
	// cerr << x << ' ' << f << '\n';
	for(int i = 1;i <= 19;i ++) fa[x][i] = fa[fa[x][i - 1]][i - 1];
	for(int i = h[x];i;i = nxt[i])
	{
		if(to[i] == f) continue;
		dfs(to[i],x);
	}
}

inline int LCA(int x,int y)
{
	if(dep[x] < dep[y]) swap(x,y);
	for(int i = 19;~i;i --) if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
	if(x == y) return x;
	for(int i = 19;~i;i --) if(fa[x][i] != fa[y][i]) x = fa[x][i],y = fa[y][i];
	return fa[x][0];
}

inline int jump(int x,int k)
{
	for(int i = 19;~i;i --) if(k & (1ll << i)) x = fa[x][i];
	return x;
}

signed main()
{
	// freopen("data.in","r",stdin);freopen("data.out","w",stdout);
	freopen("chase.in","r",stdin);freopen("chase.out","w",stdout);
	n = read();
	m = read();
	for(int i = 1,x,y;i < n;i ++)
	{
		x = read();
		y = read();
		add(x,y);
		add(y,x);
	}
	dfs(1,0);
	for(int i = 1,s,t,ss,dis1,dis2;i <= m;i ++)
	{
		ans = 0;
		s = read();
		t = read();
		ss = read();
		dis1 = dep[s] + dep[t] - 2 * dep[LCA(s,t)];
		dis2 = dep[ss] + dep[t] - 2 * dep[LCA(ss,t)];
		if(dis1 < dis2) write(dis2),con,write(t),ent;
		else 
		{
			dis1 = dep[s] + dep[ss] - 2 * dep[LCA(s,ss)];
			ans = (dis1 + 1) / 2;
			write(ans);con;
			if(dep[s] < dep[ss]) swap(s,ss),ans = dis1 - ans;
			write(jump(s,ans));ent;
		}
	}
	Blue_Archive;
}
T1 赛时大分讨
#include<bits/stdc++.h>
#define int long long
#define add(a,b) to[++ tot] = b,nxt[tot] = h[a],h[a] = tot
#define con putchar(' ')
#define ent putchar('\n')
#define Blue_Archive return 0
using namespace std;
const int N = 2e5 + 7;
const int M = 4e5 + 7;

int n;
int m;
int tot;
int ans;
int mxdp;
int h[N];
int a[N];
int id[N];
int to[M];
int fa[N];
int nxt[M];
int dep[N];
int son[N];
int siz[N];
int top[N];

inline int read()
{
	int k = 0,f = 1;
	char c = getchar();
	while(c < '0' || c > '9')
	{
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') k = (k << 3) + (k << 1) + c - '0',c = getchar();
	return k * f;
}

inline void write(int x)
{
	if(x < 0) putchar('-'),x = -x;
	if(x > 9) write(x / 10);
	putchar(x % 10 + '0');
}

inline void dfs1(int x,int f)
{
	fa[x] = f;
	siz[x] = 1;
	dep[x] = dep[f] + 1;
	mxdp = max(mxdp,dep[x]);
	for(int i = h[x];i;i = nxt[i])
	{
		if(to[i] == f) continue;
		dfs1(to[i],x);
		siz[x] += siz[to[i]];
		if(siz[to[i]] > siz[son[x]]) son[x] = to[i];
	}
}

inline void dfs2(int x,int tp)
{
	top[x] = tp;
	if(son[x]) dfs2(son[x],tp);
	for(int i = h[x];i;i = nxt[i])
	{
		if(to[i] == fa[x] || to[i] == son[x]) continue;
		dfs2(to[i],to[i]);
	}
}

inline 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;
}

inline void work_chain()
{
	for(int i = 1;i <= n;i ++) if(!fa[i])
	{
		a[1] = i;
		id[i] = 1;
		break;
	}
	int cnt = 1,op = a[1]; 
	while(son[op] != 0) 
	{	
		op = fa[op];
		a[++ cnt] = op;
		id[op] = cnt;
	}
	for(int i = 1,s,t,ss,ans;i <= m;i ++)
	{
		ans = 0;
		s = id[read()];
		t = id[read()];
		ss = id[read()];
		if((ss >= s && ss <= t) || (ss <= s && ss >= t))
		{
			ans = (abs(ss - s) + 1) / 2;
			write(ans);con;
			if(s < t) write(a[s + ans]),ent;
			else write(a[s - ans]),ent;
		}
		else 
		{
			if(s < t)
			{
				if(ss < s) 
				{
					write(t - ss);con;
					write(a[t]);ent;
				}
				if(ss > t)
				{
					ans = (ss - s + 1) / 2;
					if(ans > ss - t)
					{
						write(ans);con;
						write(a[s + ans]);ent;
					}
					else 
					{
						write(ss - t);con;
						write(a[t]);ent;
					}
				}
			}
			else 
			{
				if(ss > s) 
				{
					write(ss - t);con;
					write(a[t]);ent;
				}
				if(ss < t)
				{
					ans = (s - ss + 1) / 2;
					if(ans > t - ss)
					{
						write(ans);con;
						write(a[s + ans]);ent;
					}
					else 
					{
						write(ss - t);con;
						write(a[t]);ent;
					}
				}
			}
		}
	}
	exit(0);
}

signed main()
{
	// freopen("data.in","r",stdin);freopen("data.out","w",stdout);
	freopen("chase.in","r",stdin);freopen("chase.out","w",stdout);
	n = read();
	m = read();
	for(int i = 1,x,y;i < n;i ++)
	{
		x = read();
		y = read();
		add(x,y);
		add(y,x);
	}
	dfs1(1,0);
	dfs2(1,1);
	if(mxdp == n) work_chain();
	for(int i = 1,s,t,ss,lca,llca;i <= m;i ++)
	{
		ans = 0;
		s = read();
		t = read();
		lca = LCA(s,t);
		ss = read();
		llca = LCA(ss,lca);
		if(llca == lca) // ss 在 lca 的子树里
		{
			int lllca = LCA(ss,s);
			if(lllca == s)
			{
				if(lca == s)
				{
					ans += (dep[ss] - dep[s] + 1) / 2;
					write(ans);con;
					if((dep[ss] - dep[s] + 1) / 2 != (dep[ss] - dep[s]) / 2) ans --;
					while(dep[top[ss]] > dep[ss] - ans) ans -= dep[ss] - dep[fa[top[ss]]],ss = fa[top[ss]];
					while(ans) ss = fa[ss],ans --;
					write(ss);ent;
				}
				else 
				{
					write(dep[ss] - dep[s] + dep[t] + dep[s] - 2 * dep[lca]);con;
					write(t);ent;
				}
			}
			else if(llca == ss) // 在 s 与 lca 之间
			{
				ans = (dep[s] - dep[ss] + 1) / 2;
				write(ans);con;
				while(dep[top[s]] > dep[s] - ans) ans -= dep[s] - dep[fa[top[s]]],s = fa[top[s]];
				while(ans > 0) s = fa[s],ans --;
				write(s);ent;
			}
			else 
			{
				if(dep[ss] - dep[lllca] <= dep[s] - dep[lllca]) // ss 到 lllca 比 s 近
				{
					// cout << s << ' ' << t << ' ' << lca << ' ' << ss << ' ' << llca << ' ' << lllca << ' ' << dep[s] << ' ' << dep[t] << ' ' << dep[ss] << ' ' << dep[lllca] << '\n';
					int now = dep[s] - dep[ss] + dep[lllca];
					ans += dep[ss] - dep[lllca];
					ans += (now - dep[lllca] + 1) / 2;
					write(ans);con;
					while(dep[top[s]] > dep[s] - ans) ans -= dep[s] - dep[fa[top[s]]],s = fa[top[s]];
					while(ans) s = fa[s],ans --;
					write(s);ent;
				}
				else 
				{
					int op = LCA(ss,t);
					if(op == ss || op == t)
					{
						if(lca == t && op == t)
						{
							if(dep[ss] > dep[s])
							{
								write(dep[ss] - dep[t]);con;
								write(t);ent;
							}
							else 
							{
								ans = (dep[s] - dep[ss] + 1) / 2;
								while(dep[top[s]] > dep[s] - ans) ans -= dep[s] - dep[fa[top[s]]],s = fa[top[s]];
								while(ans) s = fa[s],ans --;
								write(s);ent;
							}
						}
						else 
						{
							int now = dep[ss] - dep[s] + dep[lllca];
							ans += dep[s] - dep[lllca];
							ans += (now - dep[lllca] + 1) / 2;
							write(ans);con;
							if((now - dep[lllca] + 1) / 2 != (now - dep[lllca]) / 2) ans --;
							while(dep[top[ss]] > dep[ss] - ans) ans -= dep[ss] - dep[fa[top[ss]]],ss = fa[top[ss]];
							while(ans) ss = fa[ss],ans --;
							write(ss);ent;
						}
					}
					else 
					{
						if(dep[s] - dep[lca] + dep[op] - dep[lca] < dep[ss] - dep[op]) 
						{
							write(dep[ss] + dep[t] - 2 * dep[op]);con;
							write(t);ent;
						}
						else 
						{
							ans += dep[ss] - dep[op];
							if(dep[s] - ans > dep[lca]) 
							{
								if(dep[s] - dep[lca] <= dep[ss] - dep[lca]) // 在 op 到 lca 链上
								{
									ans = dep[s] - dep[lca];
									int now = dep[ss] - ans;
									ans += (now - dep[lca] + 1) / 2;
									write(ans);con;
									if((now - dep[lca] + 1) / 2 != (now - dep[lca]) / 2) ans --;
									while(dep[top[ss]] > dep[ss] - ans) ans -= dep[ss] - dep[fa[top[ss]]],ss = fa[top[ss]];
									while(ans) ss = fa[ss],ans --;
									write(ss);ent;
								}
								else 
								{
									ans = dep[ss] - dep[lca];
									ans += (dep[s] - ans - dep[lca] + 1) / 2;
									while(dep[top[s]] > dep[s] - ans) ans -= dep[s] - dep[fa[top[s]]],s = fa[top[s]];
									while(ans) s = fa[s],ans --;
									write(s);ent;
								}
							}
							else  // 在 lca 与 op 的链上
							{
								int now = dep[lca] + ans - (dep[s] - dep[lca]);
								if(now > dep[op])
								{
									write(dep[ss] + dep[t] - 2 * dep[op]);con;
									write(t);ent;
								}
								ans += (dep[op] - now + 1) / 2;
								write(ans);con;
								if((now + dep[op] + 1) / 2 != (now + dep[op]) / 2) ans --;
								while(dep[top[ss]] > dep[ss] - ans) ans -= dep[ss] - dep[fa[top[ss]]],ss = fa[top[ss]];
								while(ans) ss = fa[ss],ans --;
								write(ss);ent;
							}
						}
					}
				}
			}
		}
		else 
		{
			int now = dep[s];
			if(dep[s] - dep[lca] >= dep[ss] + dep[lca] - 2 * dep[llca]) // ss 到 lca 比 s 快
			{
				now -= dep[ss] + dep[lca] - 2 * dep[llca];
				ans += dep[ss] + dep[lca] - 2 * dep[llca];
				ans += (now - dep[lca] + 1) / 2;
				write(ans);con;
				while(dep[top[s]] > dep[s] - ans) ans -= dep[s] - dep[fa[top[s]]],s = fa[top[s]];
				while(ans) s = fa[s],ans --;
				write(s);ent;
			}
			else 
			{
				ans += dep[ss] + dep[lca] - 2 * dep[llca];
				ans += dep[t] - dep[lca];
				write(ans);con;
				write(t);ent;
			}
		}
	}
	Blue_Archive;
}

T2 : 正解是异或哈希,就是将暴力中的 \(O(m)\)\(check\) 换成了 \(O(1)\) 的通过异或哈希前缀和优化后相减得 0 \(check\),可以在 \(O(n)\) 遍历时统计答案,也可以在最后用等差数列求和统计答案。

T2 正解
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long 
#define con putchar(' ')
#define ent putchar('\n')
#define Blue_Archive return 0
using namespace std;
const int N = 1e6 + 3;

int T;
int n;
int m;
int sum;
int ans;
ull a[N];
ull id[N];

inline int read()
{
	int k = 0,f = 1;
	char c = getchar();
	while(c < '0' || c > '9')
	{
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') k = (k << 3) + (k << 1) + c - '0',c = getchar();
	return k * f;
}

inline void write(int x)
{
	if(x < 0) putchar('-'),x = -x;
	if(x > 9) write(x / 10);
	putchar(x % 10 + '0');
}

signed main()
{
	// freopen("data.in","r",stdin);freopen("data.out","w",stdout);
	freopen("st.in","r",stdin);freopen("st.out","w",stdout);
	mt19937_64 myrand(time(0));
	T = read();
	while(T --)
	{
		ans = sum = 0;
		n = read();
		m = read();
		for(int i = 1;i < m;i ++) id[i] = myrand(),sum += id[i];
		id[m] = -sum;
		for(int i = 1;i <= n;i ++) a[i] = a[i - 1] + id[read()];
		sort(a + 1,a + n + 1);
		for(int i = 1,cnt = 1;i <= n;i ++)
		{
			if(a[i] != a[i-1]) cnt = 0;
			cnt ++;
			ans += cnt - 1;
		} 
		write(ans);ent;
	}
	Blue_Archive;
}
T2 赛时暴力
#include<bits/stdc++.h>
// #define int long long
#define con putchar(' ')
#define ent putchar('\n')
#define Blue_Archive return 0
using namespace std;
const int N = 1e4 + 3;
const int M = 550;

int T;
int n;
int m;
int ans;
int a[N];
int t[N];

inline int read()
{
	int k = 0,f = 1;
	char c = getchar();
	while(c < '0' || c > '9')
	{
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') k = (k << 3) + (k << 1) + c - '0',c = getchar();
	return k * f;
}

inline void write(int x)
{
	if(x < 0) putchar('-'),x = -x;
	if(x > 9) write(x / 10);
	putchar(x % 10 + '0');
}

signed main()
{
	// freopen("data.in","r",stdin);freopen("data.out","w",stdout);
	freopen("st.in","r",stdin);freopen("st.out","w",stdout);
	T = read();
	while(T --)
	{
		ans = 0;
		n = read();
		m = read();
		for(int i = 1;i <= n;i ++) a[i] = read();
		for(int len = 1,l = 1,r;len * m <= n;len ++)
		{
			bool op = 1;
			l = 1;
			r = 1;
			for(;r < l + len * m - 1;r ++) t[a[r]] ++;
			t[a[l + len * m - 1]] ++;
			while(r <= n)
			{
				op = 1;
				for(int j = 1;j <= m;j ++) if(t[j] != len) {op = 0;break;}
				ans += op;
				t[a[l]] --;
				l ++;
				r ++;
				t[a[r]] ++; 
			}
			for(int j = 1;j <= m;j ++) t[j] = 0;
		}
		write(ans);ent;
	}
	Blue_Archive;
}

T3 : 是贪心 + dp,考虑一个状态: \(dp[i][j]\) ,代表 \(i\) 个集合放前 \(j\) 个线段的最大贡献,最开始时贪心地将包含其它线段的大线段挑出来,让其取自成一集合,最后在于 dp 取最优即可。

T3 赛时假贪心
#include<bits/stdc++.h>
#define int long long
#define con putchar(' ')
#define ent putchar('\n')
#define Blue_Archive return 0
using namespace std;
const int N = 5e3 + 3;
const int INF = 1e9;

int T;
int n;
int m;
int ans;

struct miku
{
	int x;
	int y;
	int len;
}op[N],a[N];

struct mika
{
	int id;
	int len;
};

inline bool operator < (mika a,mika b){return a.len > b.len;}
inline bool operator > (mika a,mika b){return a.len > b.len;}

priority_queue<mika> jk[N];

inline int read()
{
	int k = 0,f = 1;
	char c = getchar();
	while(c < '0' || c > '9')
	{
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') k = (k << 3) + (k << 1) + c - '0',c = getchar();
	return k * f;
}

inline void write(int x)
{
	if(x < 0) putchar('-'),x = -x;
	if(x > 9) write(x / 10);
	putchar(x % 10 + '0');
}

signed main()
{
	// freopen("data.in","r",stdin);freopen("data.out","w",stdout);
	freopen("se.in","r",stdin);freopen("se.out","w",stdout);
	n = read();
	m = read();
	for(int i = 1;i <= n;i ++)
	{
		a[i].x = read();
		a[i].y = read();
		a[i].len = a[i].y - a[i].x;
	} 
	sort(a + 1,a + n + 1,[](miku a,miku b){return a.len > b.len;});
	for(int i = 1;i <= m;i ++)
	{
		op[i].x = a[i].x;
		op[i].y = a[i].y;
		op[i].len = a[i].y - a[i].x;
		jk[i].push({i,a[i].y - a[i].x});
		ans += op[i].len;
	}
	for(int i = m + 1,res,pos,jl;i <= n;i ++)
	{
		res = -INF;
		pos = 0;
		for(int j = 1;j <= m;j ++)
		{
			jl = max(max(op[i].x,a[i].x) - min(op[i].y,a[i].y),0ll) - a[j].len;
			if(jl > res)
			{
				res = jl;
				pos = j;
			}
		}
		ans -= op[pos].len;
		op[pos].x = max(op[pos].x,a[i].x);
		op[pos].y = min(op[pos].y,a[i].y);
		op[pos].len = max(op[pos].y - op[pos].x,0ll);
		// cout << i << ' ' << pos << ' ' << op[pos].x << ' ' << op[pos].y << '\n';
		ans += op[pos].len;
	}
	// for(int i = 1;i <= m;i ++) cout << op[i].len << '\n';
	write(ans);ent;
	Blue_Archive;
}

T4 : 算结论题吗?反正题解说答案是 \(m ^ n + \sum_{p\in p}{m ^ p}\) 其中 \(p\)\(B\)\(Border\)集合,我看不懂但我大为震撼。

题解直接说结论是打表猜出来的,啊????????

这里直接放上原图感受一下:

看不懂,改不了。

posted @ 2025-09-11 20:09  MyShiroko  阅读(26)  评论(0)    收藏  举报