好久没上博客骂(傻逼的)自己了呢。
今天的混更,是一道普及+/提高的题目。
https://www.luogu.org/problem/show?pid=1351(我已经放弃垃圾博客园的阉割版markdown的链接功能了
对于30% 的数据,1 < n≤ 100 ;
对于60% 的数据,1 < n≤ 2000;
对于100%的数据,1 < n≤ 200 , 000 ,0 < wi≤ 10, 000 。
我个人觉得呢,这个题的评级呢还是很准的
难呢,不是它的问题,很有可能是你的问题
这题包含了我个人的几乎所有我能想象到的能够令我大喊“Mitchie M P”的恶心地方。
说到这个,来个题外话
acg.tv/14065638
我放一下我的心路历程吧
我刚开始呢,本着热身(其实是暂时懒得动脑想)的心态,直接暴力
从每个点开始dfs,到孙子就计算,return ;
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
inline void in(int &p,char c=getchar())
{
while(c<'0' or c>'9')
c=getchar();
p=0;
while(c>='0' and c<='9')
p=p*10+c-'0',c=getchar();
}
inline void inl(long long &p,char c=getchar())
{
while(c<'0' or c>'9')
c=getchar();
p=0;
while(c>='0' and c<='9')
p=p*10+c-'0',c=getchar();
}
bool cal[2001][2001],map[2001][2001],vis[2001];//数组我都懒得开大
int n;
long long mquan,squan,w[2001];//w,权值。mquan==max quan,最大权值。squan==sum quan,权值和。
const int mo=10007;
void dfs(int st,int here,int deep)
{
if(deep>2)
return ;
vis[here]=1;
if(deep==2 and !cal[st][here])
{
long long a=w[st]*w[here];
mquan=max(mquan,a);
squan+=a;
if(squan>mo)
squan%=mo;
cal[st][here]=1;
return ;
}
for(int i=1;i<=n;i++)
if(map[here][i] and !vis[i])
dfs(st,i,deep+1);
}
int main()
{
in(n);
for(int i=0;i<n-1;i++)
{
int a,b;
in(a);in(b);
map[a][b]=map[b][a]=1;
}
for(int i=1;i<=n;i++)
inl(w[i]);
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof(vis));
dfs(i,i,0);
}
printf("%lld %lld",mquan,squan);
return 0;
}
闪闪亮亮的60分!在赛场上我觉得我已经圆满了,可以停了。
因为在后面写正解的过程中我不断产生新的problem我very担心赛场上我也这样最后爆0
所以还是加个
if(n>2001)
{
//乱写的自以为的正解
}
else
{
//直接暴力部分分
}
比较好呢!
正解应该是:
分析dfs中的一个点,它或与它相关的点产生贡献的方式只有2种:
1.自己与自己的爷爷(
爸爸的爸爸叫爷爷,爸爸的妈妈叫奶奶)
2.自己的儿子之间(哇听起来好奇怪nnsz!
这些都是dfs就可以搞定的!
爷爷,爸爸都可以顺便记录
儿子之间也很方便算:
设一个节点有N个儿子:son1,son2,son3,son4,...,sonN
(son1+son2+...+sonN)(son1+son2+...+sonN)-son12-son22-...-sonN^2=儿子之间的值(不需要再2)
儿子们中的最大联合权值,一定是由最大的单点权值X第二大的单点权值得来的
大家可以颅内感受一下是不是这样的。(是)。那么我们只需要在暴力代码上修改一点点(
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
inline void in(int &p,char c=getchar())
{
while(c<'0' or c>'9')
c=getchar();
p=0;
while(c>='0' and c<='9')
p=p*10+c-'0',c=getchar();
}
inline void inul(unsigned long long &p,char c=getchar())
{
while(c<'0' or c>'9')
c=getchar();
p=0;
while(c>='0' and c<='9')
p=p*10+c-'0',c=getchar();
}
bool /*map[20001][20001],*/vis[200001];
vector <int> map[200001];//这里因为数据的原因,垃圾的邻接矩阵不再适合,我用vector[i]存与i相连的点
int n;
unsigned long long mquan,squan,w[200001];//w,权值。mquan==max quan,最大权值。squan==sum quan,权值和。
const unsigned int mo=10007;
void dfs(int gdad,int dad,int here,int deep)
{
vis[here]=1;
unsigned long long a,max1=0,max2=0;
if(deep>=3)//如果deep小于3,说明它还没有爷爷,就不会和爷爷发生关系
{
a=w[gdad]*w[here];
mquan=max(mquan,a);
squan+=a*2;
if(squan>=mo)
squan%=mo;
}
a=0;
if(!map[here].size())//最后我才发现,这句是没用的,因为每个点都至少有1个相连的点
return ;
if(map[here].size()>2)//如果size<=2,说明它只有最多2个点,一个是自己的爸爸,另一个是自己唯一的儿子,本题并不能自交
{
for(unsigned int i=0;i<map[here].size();i++)
{
if(map[here][i]==dad)//我是你爸,请跳过别算
continue;
a+=w[map[here][i]];
if(max1<=w[map[here][i]])//大家颅内感受一下这个比对逻辑
max2=max1,max1=w[map[here][i]];
else
max2=max(max2,w[map[here][i]]);
}
/*if(a>=mo)
a%=mo;*/
//这个一定不能写!我所有的20分30分都是因为这个。如果在这里先mo,后面的结果会错,变成负数之类,即使加mo加成正数也是错的!!!
a=a*a;
for(unsigned int i=0;i<map[here].size();i++)
if(map[here][i]!=dad)//我是你爸,请跳过别算
a-=w[map[here][i]]*w[map[here][i]];
while(a<0)
a+=mo;
mquan=max(mquan,max1*max2);
squan=squan+a;
if(squan>=mo)
squan%=mo;
}
for(unsigned int i=0;i<map[here].size();i++)
if(!vis[map[here][i]])
dfs(dad,here,map[here][i],deep+1);
}
int main()
{
//freopen("1.in","r",stdin);
in(n);
for(int i=0;i<n-1;i++)
{
int a,b;
in(a);in(b);
//map[a][b]=map[b][a]=1;
map[a].push_back(b);
map[b].push_back(a);
}
for(int i=1;i<=n;i++)
inul(w[i]);
dfs(0,0,(1+n)>>1,1);//选任何节点开始dfs都可以的
printf("%lld %lld",mquan,squan);
return 0;
}
这是最终ac版,各种注释,各种特判if,太丑了
今天也是划水的一天呢