2023.10.4模拟赛总结

挂大分啦!!呜呜呜

赛时

开考先开 T 1 T1 T1,发现好像会打 25 p t s 25pts 25pts的暴力分数,然而当时没有好好想就开打了,这么多条性质竟然一条性质都没有发现,直接敲了一个 L C A + R M Q + D i j LCA+RMQ+Dij LCA+RMQ+Dij 150 150 150行超长代码(真的诠释了什么叫暴力比正解难写(╯﹏╰)(╯﹏╰) ,预估 25 p t s 25pts 25pts

然后开 T 2 T2 T2,没有任何头绪再次打起了暴力,不知道怎么了,暴力打的稀碎,调了很久才过了样例,预估 20 p t s 20pts 20pts

T 3 T3 T3一开始连题目都读不懂,后来就根据题意硬模拟,(硬到什么程度呢,就是说出现在循环里套 s o r t sort sort的行为),但还好出题人给了小数据,预估$30pt4s

T 4 T4 T4一眼 f l o y d floyd floyd,转念一想这是 T 4 T4 T4啊,再好好看了一下题,发现有去重问题,没时间写了

赛后

心态炸了,T2T飞了,挂了20分,总分55分,排名倒数┭┮﹏┭┮呜呜呜
第一次尝试写一下题解,不知道写的好不好

T1

题目简介:大小为n个点的树上有m条路径,路径的起点和终点都可以到达这条路径上的所有点,从点1出发,只能走给定的路径,求经过的最小路径数量

25pts做法,把路径起点和终点与路径上的所有点连线,跑单源最短路超级复杂的 L C A + R M Q + D i j LCA+RMQ+Dij LCA+RMQ+Dij做法,只有我这个大冤种写了吧,放一下考场的代码吧`

#include <bits/stdc++.h>
#define ll long long
#define F(i,a,b) for(int i = (a);i<=(b);i++)
#define R register
using namespace std;
inline int read() {R int x=0,t=1;R char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') t=-1;ch=getchar();}while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*t;}
const int N=1e5+10;
int n,m,p[N],f[N][40],dep[N],id[N],tot,Fa[N],dis[N];
vector<int>G[N],g[N];
inline void ADD(int x,int y)
{
	G[x].push_back(y);
	return;//树边 
}
inline void add(int x,int y)
{
	g[x].push_back(y);//新边(巴士) 
	return;
}
void dfs(int u,int fa)
{
	Fa[u]=fa;
	dep[u]=dep[fa]+1;
	f[++tot][0]=u;
	id[u]=tot;
	for(int i = 0;i<G[u].size();i++){
		int v=G[u][i];
		if(v!=fa){
			dfs(v,u);
			f[++tot][0]=u;
		}
	}
	return;
}
void find(int u,int q,int LCA)
{
	if(u!=q)
	{
	//	cout << q << "-->>" << u << '\n';
		add(q,u);//建边 
	}
	if(u==LCA)
	{
		return;	//到头了不能再上了 
	}
	find(Fa[u],q,LCA);
	return;
}
void find1(int u,int q,int LCA)
{
	if(u!=q && u!=LCA)//LCA再find里面加过了 
	{
	//	puts("114514");
//		cout << q << "-->>" << u << '\n';
		add(q,u);//建边 
	}
	if(u==LCA)
	{
		return;	//到头了不能再上了 
	}
	find1(Fa[u],q,LCA);
	return;
}
int cmp(int x,int y)
{
	return dep[x]<dep[y] ? x : y;//取深度更浅的 
}
inline int lca(int u,int v)
{
	if(id[u]>=id[v]) swap(u,v);
	int k=log2(id[v]-id[u]+1);
	return cmp(f[id[u]][k],f[id[v]-(1<<k)+1][k]);
}
struct Node
{
	int id,dis;
};
bool operator <(const Node &x,const Node &y)
{
	return x.dis<y.dis;
}
void dij()
{
	priority_queue<Node>q;
	dis[1]=0;
	memset(dis,0x3f,sizeof dis);
	dis[1]=0;
	q.push({1,0});
	//cout << dis[1] << '\n';
	while(q.size())
	{
		int u=q.top().id;
		q.pop();
		for(int i = 0;i<g[u].size();i++){
			int v=g[u][i];
			if(dis[v]>dis[u]+1){
				dis[v]=dis[u]+1;
				q.push({v,dis[v]});
			}
		}
	}
	
	return;
}
inline void solve()
{
	bool flag=0;
	n=read(),m=read();
	F(i,1,n-1){
		p[i]=read();
		if(p[i]!=i) flag=1;
		ADD(i+1,p[i]);
		ADD(p[i],i+1);//树边建边 
	}
	
	dfs(1,-1);//遍历树,预处理LCA
	for(int j = 1;(1<<j)<=tot;j++)//st表预处理LCA 
	{
		for(int i = 1;i<=tot;i++)
		{
			f[i][j]=cmp(f[i][j-1],f[i+(1<<(j-1))][j-1]);
		}
	} 
	F(i,1,m){
		int ui=read(),vi=read();
		find(ui,ui,lca(ui,vi));
		find1(vi,ui,lca(ui,vi));//提取路上的节点与ui连边
		find(vi,vi,lca(ui,vi));
		find1(ui,vi,lca(ui,vi));//提取路上的节点与vi连边	
	}
	dij();
	F(i,1,n)
	{
		if(dis[i]>5e6) cout << -1 << " ";
		else
		cout << dis[i] << ' ';
	}
	//printf("%d\n",n);
}
int main()
{
	freopen("A.in","r",stdin);
	freopen("A.out","w",stdout);
	solve();
	return 0;
}

100pts做法:考虑从dij下手,发现边权都是1,dij等价于bfs。思考跑bfs的过程易得,一个点被遍历到了,那么它的树上所有祖先都被遍历过了。所以遍历到一个路径的起点或终点时,遍历这条路径,发现有点被遍历过了就停下,这个过程是线性的,顶多是遍历路径时的两个端点会多次遍历,于是复杂度为 O ( n + m ) O(n+m) O(n+m)可以通过此题。
代码:

#include <bits/stdc++.h>
#define ll long long
#define F(i,a,b) for(int i = (a);i<=(b);i++)
#define R register
using namespace std;
inline int read() {R int x=0,t=1;R char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') t=-1;ch=getchar();}while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*t;}
const int N=1e6+10;
int n,m,f[N],fa[N];
queue<int>q;
vector<int>g[N];
inline void work(int x,int v)
{
	while(f[x]==-1)//尚未进栈 
	{
		f[x]=v;
		q.push(x);
		x=fa[x];
	}
	return;
}
inline void solve()
{
	n=read(),m=read();
	F(i,2,n) fa[i]=read();
	F(i,1,m){
		int ui=read(),vi=read();
		g[ui].push_back(vi);
		g[vi].push_back(ui);
	}
	memset(f,-1,sizeof f);
	f[1]=0;
	q.push(1);
	while(q.size())
	{
		int u=q.front();
		q.pop();
		for(int i = 0;i<g[u].size();i++)
		{
			int v=g[u][i];
			work(v,f[u]+1);
		}
	}
	F(i,1,n) cout << f[i] << " ";
}
int main()
{
	freopen("A.in","r",stdin);
	freopen("A.out","w",stdout);
	solve();
	return 0;
}

T2

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
数据强制在线,卡掉了奇奇怪怪的离线做法。可以看到,每个地点的最后的大合照其实都是固定的,都和初始在校门口的一样。正难则反,考虑从结束状态慢慢删除人,用链表维护,复杂度 O ( n q ) O(nq) O(nq),可以通过此题。
代码:

#include <bits/stdc++.h>
#define int long long
#define ll long long
#define F(i,a,b) for(int i = (a);i<=(b);i++)
#define R register
using namespace std;
inline int read() {R int x=0,t=1;R char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') t=-1;ch=getchar();}while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*t;}
const int N=5e5+10;
int n,m,h[N],a[N],p[N],now,l[N],r[N];
inline int ask(int x,int y)
{
	if(x==0 || y==n+1) return 0;
	return (a[x]-a[y])*(a[x]-a[y]);
}
inline int work()
{
	for(int i = 1;i<=n;i++) l[i]=i-1,r[i]=i+1;
	int pos=now,w=0,ans=0;
	for(int i = 1;i<n;i++)
	{
		w+=(a[i]-a[i+1])*(a[i]-a[i+1]);//结束状态就是校门口的初始状态 
	}
	ans+=w;
	if(pos==0) pos=n;
	for(int i = 1;i<=n;i++)
	{
		int t=p[pos];
		w-=ask(l[t],t);
		w-=ask(t,r[t]);
		w+=ask(l[t],r[t]);
		r[l[t]]=r[t];
		l[r[t]]=l[t];
		ans+=w;
		pos--;
		if(pos==0) pos=n;
	}
	return ans;
}
inline void solve()
{
	n=read(),m=read();
	F(i,1,n) a[i]=read();
	F(i,1,n) p[i]=read();
	int k,lst=work();
	cout << lst << '\n';
	F(i,1,m)
	{
		k=read();
		now+=lst+k;
		now%=n;
		lst=work();
		cout << lst << '\n';
	}
}
signed main()
{
   freopen("C.in","r",stdin);
    freopen("C.out","w",stdout);
	solve();
	return 0;
}


T 3 T3 T3 T 4 T4 T4在下一篇博客里写。

第一回写题解,紧张捏

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值