洛谷 11 月月赛 I T2 P7043 「MCOI-03」村国

题目背景

\(\texttt{What did this player dream?}\)

他梦见了什么?

\(\texttt{This player dreamed of sunlight and trees.Of fire and water.}\)

他梦见了阳光与树木。梦见了火与水。

\(\texttt{It dreamed it created. And it dreamed it destroyed. It dreamed it hunted, and was hunted. It dreamed of shelter.}\)

他梦见他的创造,亦梦见他毁灭。它梦见他在狩猎,亦梦见被猎捕。他梦见温馨的居所。

\(\texttt{Hah, the original interface. A million years old, and it still works.But what true structure did this player create, in the reality behind the screen?}\)

哎,那原始的界面。经历百万年的岁月,它依然在工作。只是他在屏幕后的真实里,到底创造了什么真实的世界呢?

题目描述

C 国一共有 \(N\) 个村庄,\(N−1\) 条道路。这些道路都可以双向通行。保证小 S 可以从一座村庄到其他任何一座村庄。这 \(N\) 个村庄编号为 \(1\)\(N\)

刚开始小 S 对第 \(i\) 个村庄的好感值为 \(A_i\)

小 S 的假期一共有 \(M\) 天,他会在 C 国旅行一共 \(M\) 天。每一天他会选择来到当前好感值最高的村庄。如果有好感值相同的村庄,他会选择编号最小的村庄。假设这一天他来到村庄 \(X\),那么这一天结束后,与村庄 \(X\) 直接相邻所有村庄的好感值都会增加 \(1\)。即能从 \(X\) 出发仅经过一条道路到达的村庄好感值会增加 \(1\)。因为小 S 已经在村庄 \(X\) 待过一天了,所以这一天结束后村庄 \(X\) 的好感值并不会增加。

现在小 S 想要知道经过 \(M\) 天的旅行后好感值最高的村庄。

如果有多个好感值最高的村庄,输出编号最小的。

输入格式

本题单个测试点包含多组测试数据。

第一行一个正整数 \(T\) 表示数据组数。

对于每组数据:

第一行包括两个正整数 \(N,M\),表示村庄个数和旅行天数。

接下来一行 \(N\) 个整数,第 \(i\) 个整数表示第 \(i\) 座村庄的好感值 \(A_i\)

接下来 \(N−1\) 行。每行两个整数 \(x,y\) 表示村庄 \(x\) 和村庄 \(y\) 之间有一条双向通行的道路。

输出格式

一个整数表示 \(M\) 天结束后好感值最高的村庄的编号。如果有多个好感值最高的村庄,输出编号最小的。

输入输出样例

输入 #1

2
2 3
2 6
1 2
3 5
2 6 4
1 3
2 3

输出 #1

2
3

说明/提示

样例说明

对于第一组数据,小 S 在 \(2\) 号村庄旅行了 \(3\) 天,结束时村庄 \(1,2\) 的好感值分别为 \(5,6\)。所以答案输出 \(2\)

对于第二组数据,结束时三个村庄的好感值分别为 \(3,7,8\),所以答案输出 \(3\)

数据规模与约定

对于 \(100\%\) 的数据,\(1 \le N \le 2\times10^6\),\(1 \le M \le 10^{18}\),\(1 \le A_i \le 2^{31}-1\),\(1 \le T \le 10\).

测试点编号 \(A_i \le\) \(∑ N \le\) \(M \le\) 测试点分值
\(1\) \(10\) \(20\) \(10\) \(5\)
\(2\) \(10^2\) \(2*10^2\) \(10^2\) \(10\)
\(3\) \(10^3\) \(2*10^3\) \(10^3\) \(15\)
\(4\) \(10^5\) \(2*10^5\) \(10^5\) \(25\)
\(5\) \(2*10^6\) \(45\)

提示

本题输入量较大,请使用较快的读入方式。

分析

第一想法,模拟,\(15pts\)

考虑用线段树等优化,可以优化到\(30pts\)的好成绩……

线段树

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#define re register
#define debug printf("Now is %d\n",__LINE__);
using namespace std;

template<class T>inline void read(T&x)
{
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
}
inline LL read()
{
	LL x=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x;
}
int G[55];
template<class T>inline void write(T x)
{
    int g=0;
    if(x<0) x=-x,putchar('-');
    do{G[++g]=x%10;x/=10;}while(x);
    for(re int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n');
}
LL t;
LL n,m,a[2000010];
LL head[4000010],nxt[4000010],ver[4000010];
LL cnt;
IL void insert(int x,int y)
{
	nxt[++cnt]=head[x];
	head[x]=cnt;
	ver[cnt]=y;
}
struct node
{
	int l,r,mx,lmax;
	#define l(x) b[x].l
	#define r(x) b[x].r
	#define mx(x) b[x].mx
	#define lmax(x) b[x].lmax
}b[2000010];
IL void pre()
{
	memset(head,0,sizeof(head));
	memset(a,0,sizeof(a));
	memset(nxt,0,sizeof(nxt));
	memset(ver,0,sizeof(ver));
}
IL void upd(int p)
{
	if(mx(p<<1)>=mx(p<<1|1))
	{
		mx(p)=mx(p<<1);
		lmax(p)=lmax(p<<1);
	}
	else
	{
		mx(p)=mx(p<<1|1);
		lmax(p)=lmax(p<<1|1);
	}
}
void build(int l,int r,int p)
{
	l(p)=l;
	r(p)=r;
	if(l==r)
	{
		mx(p)=a[l];
		lmax(p)=l;
		return;
	}
	int mid=l+r>>1;
	build(l,mid,p<<1);
	build(mid+1,r,p<<1|1);
	upd(p);
}
void change(int p,int x)
{
	if(l(p)==r(p))
	{
		mx(p)++;
		return;
	}
	int mid=l(p)+r(p)>>1;
	if(x<=mid) change(p<<1,x);
	else change(p<<1|1,x);
	upd(p);
}
int main()
{
	t=read();
	while(t--)
	{
		n=read();
		m=read();
		pre();
		for(re int i=1;i<=n;i++) read(a[i]);
		for(re int i=1,x,y;i<n;i++)
		{
			x=read();
			y=read();
			insert(x,y);
			insert(y,x);
		}
		build(1,n,1);
		for(re int i=1;i<=m;i++)
		{
			for(re int j=head[b[1].lmax];j;j=nxt[j])
			{
				change(1,ver[j]);
			}
		}
		cout<<b[1].lmax<<endl;
	}
	return 0;
}

优化

数据中的\(M \le 10^{18}\)告诉我们这是一道结论题。

不难发现,模拟过程分为三个阶段:

  • 与最大点权点相邻的点的权值正在增加
  • 初始有最大点权的点的某一个相邻的点的点权超过了它的点权
  • 这两个点的权值在不断的抽搐轮流增加

所以先判断\(M\)天的时候,与最大点权点相邻的点的权值有没有超过最大权值。如果没超过,那么第\(M\)天的最大权值点还是初始的最大权值点。

如果超过了,则用\(M-i\)的奇偶性来判断到底是哪一个点即可。

注意要特判n==1,只有一个点的时候,最大权值点始终是它自己

Code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#define re register
#define debug printf("Now is %d\n",__LINE__);
using namespace std;

template<class T>inline void read(T&x)
{
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
}
inline int read()
{
	int x=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x;
}
int G[55];
template<class T>inline void write(T x)
{
    int g=0;
    if(x<0) x=-x,putchar('-');
    do{G[++g]=x%10;x/=10;}while(x);
    for(re int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n');
}
int head[4000010],nxt[4000010],ver[4000010],cnt;
void insert(int x,int y)
{
	nxt[++cnt]=head[x];
	ver[cnt]=y;
	head[x]=cnt;
	
	nxt[++cnt]=head[y];
	ver[cnt]=x;
	head[y]=cnt;
}
LL t,n,m,pos,mx,cpos,cmx;
int w[2000010];
int main()
{
	t=read();
	while(t--)
	{
		n=read();
		cin>>m;
		cmx=mx=-1;
		for(re int i=1;i<=n;i++) 
		{
			w[i]=read();
			if(w[i]>mx) mx=w[i],pos=i;
		}
		for(re int i=1;i<n;i++)
		{
			insert(read(),read());
		}
		for(re int i=head[pos];i;i=nxt[i])
		{
			if(w[ver[i]]>cmx) cmx=w[ver[i]],cpos=ver[i];
		}
		if(n==1)
		{
			cout<<1<<endl;
			continue;
		}
		re LL i=1;
//		for(i=1;i<=m;i++)
//		{
//			if(mx==cmx) break;
//			if(cmx>mx) mx++;
//			else if(cmx<mx) cmx++;
//			else if(pos<cpos) cmx++;
//			else if(pos>cpos) mx++;
//		}
		i=mx-cmx-1;
		if(m<=i)
		{
			cout<<pos<<endl;
			continue;
		}
		cmx=mx;
//		if(i==m+1)
//		{
//			if(cmx>mx) cout<<cpos<<endl;
//			else if(mx>cmx) cout<<pos<<endl;
//			else cout<<min(pos,cpos)<<endl;
//		}
//		else
		{
			if(pos>cpos) swap(pos,cpos);
			if((m-i+1)&1) cout<<cpos<<endl;
			else cout<<pos<<endl;
		}
		
		
		
	}
	
	return 0;
}

相信你可以从中看到很多暴力的痕迹吧~

posted @ 2020-11-02 09:33  Vanilla_chan  阅读(153)  评论(0)    收藏  举报