洛谷 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;
}
相信你可以从中看到很多暴力的痕迹吧~

浙公网安备 33010602011771号