【EOJ Monthly】2021.2

1.比赛链接:EOJ Monthly 2020.2
2.比赛情况:A题磕磕碰碰也算是拿了100(看错了从任意起点出发);B题图论蒟蒻选手直接把直链和树都当成树,写一个朴素LCA暴力骗38分闪人;C题直接在10!下找到所有可能的排列去构造线段树,然后判断是否满足所给定的区间骗23分闪人;D一分没得。
3.比赛总结:感觉B题没做出来对不起自己刷的PAT…


A:昔我往矣

  这道题我的做法是采用LCA先找两个结点的最近公共祖先,然后不断地往里面加入新的点。①很明显,第一轮两个结点需要走过的距离是 d i s t [ A ] + d i s t [ B ] − 2 ∗ d i s t [ l c a ( A , B ) ] dist[A]+dist[B]-2*dist[lca(A,B)] dist[A]+dist[B]2dist[lca(A,B)]; ②但我们找到了前 i i i个结点的LCA时,考虑如何加入第 i + 1 i+1 i+1个结点。我们找 i i i个结点的最近公共祖先结点 l c a i lca_i lcai i + 1 i+1 i+1个结点的最近公共祖先 l c a i + 1 lca_{i+1} lcai+1,然后判断:(i)如果 l c a i = l c a i + 1 lca_i=lca_{i+1} lcai=lcai+1,那说明第 i − 1 i-1 i1个结点在以 l c a i lca_i lcai为根节点的子树当中,我们要往前搜索所有的结点,找到与当前第 i + 1 i+1 i+1个结点的所有最近公共祖先结点中,层次最深的那一个 t m p tmp tmp,然后贡献加上 d i s t [ a [ i + 1 ] ] − d i s t [ t m p ] dist[a[i+1]]-dist[tmp] dist[a[i+1]]dist[tmp]这样才不会造成路径的重复计数);(ii)如果 l c a i ≠ l a c i + 1 lca_i\ne lac_{i+1} lcai=laci+1,说明第 i + 1 i+1 i+1个结点不在以 l c a i lca_i lcai为根节点的子树中,直接计算路径即可。

#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
const int maxn=5e4+5;
int n,m;
struct Edge{
    
    
	int to,next,weight;
}e[maxn<<1];
int head[maxn],tot=0,a[10],dis[maxn];
void addedge(int x,int y,int w)
{
    
    
	e[++tot].to=y;
	e[tot].weight=w;
	e[tot].next=head[x];
	head[x]=tot;
}
int fa[maxn][25],lg[maxn],dep[maxn];
void DFS(int now,int father,int dist)
{
    
    
	fa[now][0]=father,dep[now]=dep[father]+1;
	dis[now]=dist;
	for(int i=1;(1<<i)<=dep[now];++i)
		fa[now][i]=fa[fa[now][i-1]][i-1];
	for(int i=head[now];i;i=e[i].next)
		if(e[i].to!=father) DFS(e[i].to,now,dist+e[i].weight);
} 
int LCA(int x,int y)
{
    
    
	if(dep[x]<dep[y]) swap(x,y);
	while(dep[x]>dep[y]) x=fa[x][lg[dep[x]-dep[y]]-1];
	if(x==y) return x;
	for(int k=lg[dep[x]]-1;k>=0;--k)
		if(fa[x][k]!=fa[y][k]) x=fa[x][k],y=fa[y][k];
	return fa[x][0];
}
void init_lg(){
    
    for(int i=1;i<=n;++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);} 
int main()
{
    
    
	close;cin>>n;
	for(int i=0;i<n-1;++i)
	{
    
    
		int x,y,w;cin>>x>>y>>w;
		addedge(x,y,w);addedge(y,x,w);
	}
	init_lg();DFS(0,-1,0);cin>>m;
	while(m--)
	{
    
    
		for(int i=0;i<5;++i) cin>>a[i];
        int A=a[0],B,minnum=0;
		for(int i=1;i<=4;++i)
		{
    
    
			B=a[i];
			int tmp=LCA(A,B);
            if(tmp==A){
    
    
                int now=A;
                for(int j=0;j<i;++j){
    
    
                    int cur_num=LCA(a[i],a[j]);
                    if(dep[cur_num]>dep[now]) now=cur_num;
                }
                minnum+=dis[B]-dis[now];
            }
            else{
    
    
                minnum+=dis[A]+dis[B]-dis[tmp]*2;
                A=tmp;
            }
		}
		cout<<minnum<<"\n";
	}
}

B:杨柳依依

  考察最短路问题,但要求解最短路中经过结点 i i i的所有路径的条数
  两遍BFS求最短路。第一遍BFS找到起点 s s s到点 i i i的方案数,第二遍BFS找到终点 t t t到起点 i i i的方案数,相乘就是总的可能路线。注意写法(第一遍BFS会给一些不在最短路径上的结点也赋值了,第二遍就一定要保证不在最短路径上的点值一定是0)。

#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
const int maxn=5e3+100;
const double eps=1e-6;
vector<int> G[maxn];
int num1[maxn],num2[maxn],d[maxn];
double ans[maxn];
int main()
{
    
    
	close;int n,m;cin>>n>>m;
	for(int i=1;i<=m;++i)
	{
    
    
		int x,y;cin>>x>>y;
		G[x].push_back(y);
		G[y].push_back(x);
	}
	int k;cin>>k;
	for(int i=1;i<=k;++i)
	{
    
    
		int x,y;cin>>x>>y;
		memset(num1,0,sizeof(num1));
		memset(num2,0,sizeof(num2));
		memset(d,0,sizeof(d));
		queue<int> q;
		d[x]=0,num1[x]=1;q.push(x);
		while(!q.empty()){
    
    
			int cur=q.front();q.pop();
			if(cur==y) break;
			for(auto tmp:G[cur]){
    
    
				if(num1[tmp]==0) q.push(tmp),d[tmp]=d[cur]+1;
				if(d[tmp]==d[cur]+1) num1[tmp]+=num1[cur]; 
			}
		}
		while(!q.empty()) q.pop();
		num2[y]=1;q.push(y);
		while(!q.empty()){
    
    
			int cur=q.front();q.pop();
			if(cur==x) break;
			for(auto tmp:G[cur]){
    
    
				if(d[tmp]==d[cur]-1){
    
    
					if(num2[tmp]==0) q.push(tmp);
					num2[tmp]+=num2[cur];
				}
			}
		}
		for(int i=0;i<n;++i) ans[i]+=1.0*num2[i]*num1[i]/num2[x];
	}
	int pos=0;
	for(int i=1;i<n;++i) if(ans[i]-ans[pos]>=eps) pos=i;
	cout<<pos;
}

C:今我来思

D:雨雪霏霏

猜你喜欢

转载自blog.csdn.net/CUMT_William_Liu/article/details/113732507