10.4 考试总结

\(\large 10.4 考试总结\)

题面下载

得分情况

T1估分 T1实分 T2估分 T2实分 T3估分 T3实分 T4估分 T4实分 总估分 总实分
40 100 10 11 40 40 20 8 110 159

时间分配

计划:

T1(1hour)+T2(1hour)+T3(0.5hour)+T4(1hour)+所有代码检查(0.5hour)

实施:

基本一致,T2完全没有思路基本没用什么时间

核心问题与改进计划

问题:

T2没有一点思路,知识点短板明显,数据结构掌握不牢

计划:

查漏补缺,按照计划上写的去练习bbcoj上的题目,巩固知识点,加强代码能力

题目分析

\(\textup{\textbf{{\color{black}兄弟们(brother)}}}\)

思路

考场上打的是不严格\(O(nmlog_n)\),即暴力+倍增,应该是40分代码,结果AC了。
正解是用DFS序记录每一棵子树对应的编号区间。再将节点按照高度分类,高度相同的放到同一个vector。
求k级祖先时,用倍增。这样我们可以在 \(O(log_n)\)里求出祖先,然后获得祖先对应子树的编号区间,二分查找即可。
时间复杂度严格\(O(mlog_n)\)

考场code:

#include<bits/stdc++.h>
using namespace std;
inline int read() {
	int s=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
	return s*f;
}
const int N=1e5+5;
int n,m;
int ver[N<<1],head[N],ne[N<<1],tot;
vector<int> v[N];
int vis[N],q[N],fa[N],scc[N],sz[N],dep[N],f[N][30];
int cnt,ans;
void add(int x,int y){
	ver[++tot]=y,ne[tot]=head[x],head[x]=tot;
}
void dfs(int x,int fat,int topp){
//	fa[x]=fat;
	f[x][0]=fat;
	for(int i=1;i<=18;i++) 
		f[x][i]=f[f[x][i-1]][i-1];
	scc[x]=topp;
	dep[x]=dep[fat]+1;
//	cout<<x<<' '<<dep[x]<<'\n';
	for(int i=head[x];i;i=ne[i]){
		int y=ver[i];
		if(vis[y]) continue;
		vis[y]=1;
		dfs(y,x,topp);
	}
}
int nk(int x,int k){
	for(int i=18;i>=0;i--)
		if((1<<i)<=k) k-=(1<<i),x=f[x][i];
	return x;
}
bool find(int x,int g){
	for(int i=18;i>=0;i--)
		if(dep[f[x][i]]>=dep[g]) x=f[x][i];
	if(x==g) return 1;
	return 0; 
	
	//暴力
//	if(dep[x]<dep[g]) return 0;
//	if(fa[x]==0) return 0;
//	if(fa[x]==g) return 1;
//	return find(fa[x],g);
}
int main(){
	freopen("brother.in","r",stdin);
	freopen("brother.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++){
		int x=read();
		if(x!=0) add(x,i);
		else q[i]=1;
	} 
	for(int i=1;i<=n;i++) if(q[i]) dfs(i,0,++cnt);
	for(int i=1;i<=n;i++) v[dep[i]].push_back(i);
	dep[0]=-1;
	m=read();
	while(m--){
		int x=read(),k=read();
		if(k>dep[x]-1){
			printf("0\n");
			continue;
		}
		int p=nk(x,k);
//		cout<<p<<'\n';
		ans=0;
//		cout<<find(x,p)<<'\n';
		for(int i=0;i<v[dep[x]].size();i++){
			int y=v[dep[x]][i];
			int q=find(y,p);
			if(scc[y]==scc[x] && q==1) ans++;
//			cout<<y<<' ';
		}
//		cout<<'\n';
		printf("%d\n",ans-1);
	
	}
	return 0;
}

\(\textup{\textbf{{\color{black}Vifact与猫猫(cat)}}}\)

思路

考场思路:

多想一会儿就应该知道这是道线段树分治。但是我肯定是不可能调出来的,所以果断骗分。

正解思路:

如果只考虑\(d_i\),那么i作为右端点,可行的左端点的选择区间是单调增的,记为\(g_i\)可以用线段树在 \(O(nlog_n)\)内求出。
考虑分治,当前区间[l,r]中\(c_i\)的最大值作为分治中点mid。
考虑\(d_i\)的限制,每次\(g_i\)>当前左端点时,用线段树更新答案。

code:


\(\textup{\textbf{{\color{black}数论计算(count)}}}\)

思路

考场思路

一眼完全背包(40pts),感觉有问题,换成DFS暴力搜索(依旧40pts)。没什么太大区别。

正解思路

\(\color{black}{这道题}\) 十分相似
同余最短路

code:

\(\textup{\textbf{{\color{black}黑白图(dye)}}}\)

思路

二分图染色

code:

posted @ 2025-10-04 16:28  Austin0928  阅读(6)  评论(0)    收藏  举报