考试心得
今天的考试应该还算在状态的吧,第一题虽然在lemon上面只有30分,但是在洛谷上是A掉了,emmm…虽然是这样但还是要注意不要开太大才是,毕竟不怕一万就怕万一嘛…还是要注意一下就是…
第一题
题面
从前有一棵树,确定一个根节点,最大化所有点深度之和
输入
第一行n 接下来n - 1 行表示树的每条边
输出
一个整数,表示根节点编号
Sample Input
8
1 4
5 6
4 5
6 7
6 8
2 4
3 4
Sample Output
7
AC思路
这题据说是dp。。。但是我用dfs过了,思路简单来说就是先以1为根建一棵树,然后按照深度排序,最后一层层枚举它自己是根节点的情况,最后算出最大值,按字典序最小的值输出就可以了
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long long tot=0;
long long head[1000005];
long long dian[1000005];
long long fa[1000005];
long long vis[1000005];
long long he[1000050];
long long deep[1000050];
long long ans=0,j=0;
long long shu[1000500];
long long n;
struct node
{
long long biao;
}d[1000009];
struct nd
{
long long f,t,nt;
}e[3000009];
long long read()
{
char a;
long long sum=0;
a=getchar();
while(a>'9'||a<'0')a=getchar();
while(a>='0'&&a<='9')sum=sum*10+a-'0',a=getchar();
return sum;
}
long long build(long long f,long long t)
{
tot++;
e[tot].f=f;
e[tot].t=t;
e[tot].nt=head[f];
head[f]=tot;
}
long long cmp(const node &a,const node &b)
{
if(deep[a.biao]<deep[b.biao])return 1;
return 0;
}
long long sp(long long u,long long shen)
{
vis[u]=1;
deep[u]=shen;
he[u]=shen;
d[u].biao=u;
dian[u]=1;
for(long long i=head[u];i;i=e[i].nt)
{
long long v=e[i].t;
if(!vis[v])
{
fa[v]=u;
sp(v,shen+1);
he[u]+=he[v];
dian[u]+=dian[v];
}
}
}
int main()
{
n=read();
for(long long i=1;i<n;i++)
{
long long a,b;
a=read();
b=read();
build(a,b);
build(b,a);
}
sp(1,1);
sort(d+1,d+n+1,cmp);
shu[1]=he[1];
ans=shu[1];
j=1;
for(long long i=2;i<=n;i++)
{
long long u=he[d[i].biao]-dian[d[i].biao]*deep[fa[d[i].biao]];
long long y=shu[fa[d[i].biao]]-u-dian[d[i].biao];
shu[d[i].biao]=y+dian[fa[d[i].biao]]-dian[d[i].biao]+u;
dian[d[i].biao]=dian[fa[d[i].biao]];
if(shu[d[i].biao]>ans)ans=shu[d[i].biao],j=d[i].biao;
}
cout<<j<<endl;
}
第二题
题面
“秋名山上行人稀, 常有车神较高低。如今车道依旧在, 不见当年老司机”
从前有一位车神,要行驶秋名山上的一条公路。沿着公路一次站着n 个观众,第i 个观众
对他的喜欢程度为a[i]。如果a[i]<0,说明这个观众讨厌他,有可能在他经过时搞一些危险的
事情,危及他的安全。
于是,车神想选出两段路,这两段路对应的两段观众的喜欢值之和最大。这样的话,车神
在这两段路行驶就最为安全,可以适当降低警惕。
你的任务就是计算选出的这两段观众喜欢值之和的最大值。
输入
第一行:N,表示有N 个人站成一排观看比赛
第二行:有N 个数字,表示每个人的喜欢值
输出
输出选出的这两段观众喜欢值之和的最大值。注意一定要选出两段观众,每段观众至少一人。
样例输入
7
4 -5 3 -1 11 -2 -1
样例输出
17
AC思路
这道题据说也是dp。。。我开始想既然是dp,那就设定一个i为中间值就可以了,结果鬼使神差地写着写着就变成一个分别从左到右和从右到左递推最大值的做法,然后枚举中间节点,最后没用dp就A了
代码
#include<iostream>
#include<cstdio>
using namespace std;
long long you[70000];
long long n;
long long ll[70000];
long long jj[70000];
long long ans=0;
long long mmax=0;
long long read()
{
char a;
long long sum=0,f=1;
a=getchar();
while(a>'9'||a<'0')
{
if(a=='-')f=-1;
a=getchar();
}
while(a>='0'&&a<='9')sum=sum*10+a-'0',a=getchar();
return sum*f;
}
int main()
{
//freopen("safe.in","r",stdin);
//freopen("safe.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)you[i]=read();
ans=0,mmax=you[1];
for(int i=1;i<=n;i++)
{
if(ans+you[i]<you[i])ans=you[i];
else ans+=you[i];
mmax=max(mmax,ans);
jj[i]=mmax;
}
ans=0,mmax=you[n];
for(int i=n;i>=1;i--)
{
if(ans+you[i]<you[i])ans=you[i];
else ans+=you[i];
mmax=max(mmax,ans);
ll[i]=mmax;
}
ans=jj[1]+ll[2];
for(int i=1;i<n;i++)
{
ans=max(ans,jj[i]+ll[i+1]);
}
cout<<ans<<endl;
}
第三题
题面
从前有n 个人,愉快地生活在一起。直到有一天他们学会了互黑。
每个人都有且仅有一个黑的目标(这个目标有可能是他自己,这叫自黑)。对于那些没人黑
的人(其他所有人黑的目标都不是他),他们想刷存在感,于是可以选择放弃黑自己的目标而选
择自黑(也就是二选一)。
注意只有一开始没有作为任何人的目标的人才可以放弃目标选择自黑。
规定轮到某个没有被黑的人时,必须且仅能黑一次。所有被黑过的人都不能再黑别人了。
当然一个人可以被黑很多次。
由你来决定一个顺序,大家按照顺序行动。求问最后被黑的人数的最小值和最大值。
输入
第一行一个整数n
第二行n 个整数,第i 个整数ai 表示第i 个人的目标,满足1< ai < n
输出
两个整数,依次表示最小值和最大值
样例输入
8
2 3 2 2 6 7 8 5
样例输出
3 6
AC思路
太难了看的似懂非懂,就暂时只放个代码了(逃
代码
#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e6+8;
int n,head[N],tot,L,R,h[N],que[N],cir[N],die[N];
int ma,mi,hei[N],rd[N],rudu[N],cnt,cir_cnt,len,q[N];
bool vis[N],inq[N],incir[N],_die[N];
//inq在队列,incir在圈内;
struct node
{
int next,to;
}e[N<<1];
void add(int x,int y)
{
tot++;
e[tot].next=head[x],head[x]=tot,e[tot].to=y;
tot++;
e[tot].next=head[y],head[y]=tot,e[tot].to=x;
}//双向图;
int read()
{
int sum=0;
char ch=getchar();
while(ch>'9'||ch<'0') ch=getchar();
while(ch>='0'&&ch<='9') sum=sum*10+ch-'0',ch=getchar();
return sum;
}
void qiumin()
{
int l=1,r=R;
while(l<=r)
{
int u=que[l++];
if(_die[u]||_die[hei[u]])continue;//已经黑过了;
_die[hei[u]]=1;//没黑过就黑;
if(incir[hei[u]])que[++r]=hei[hei[u]];//判断是否在环内;
}
if(r+cir_cnt==cnt)mi+=(cir_cnt+1)>>1;
for(int i=1;i<=cnt;i++)mi+=_die[h[i]];
}
void qiumax()
{
if(cir_cnt+len==cnt)
{
if(cir_cnt==1) ma+=cnt;
else ma+=cnt-1;
return;
}
int x=0;
for(int i=1;i<=len;++i)
if(!incir[hei[que[i]]]) rudu[hei[que[i]]]--;
for(int i=len+1;i<=R;++i) if(!rudu[que[i]]) die[que[i]]=-1;
for(int i=1;i<=cnt;++i) if(die[h[i]]==-1) x++;
ma+=cnt-x;
}
void solve(int rt)
{
int l=1,r=1;h[l]=rt;
while(l<=r)
{
int u=h[l++];vis[u]=1;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(!vis[v]) vis[v]=1,h[++r]=v;
}
}
cnt=r,len=0;
for(int i=1;i<=r;i++)if(!rd[h[i]]) que[++len]=h[i],inq[h[i]]=1;
if(!len)
{
if(r==1)ma++,mi++;
else ma+=r-1,mi+=(r+1)/2;
return;
}//成环直接可以算出来;
L=1,R=len;
while(L<=R)
{
int u=que[L++];
rd[hei[u]]--;
if(!rd[hei[u]]) que[++R]=hei[u],inq[hei[u]]=1;
}
cir_cnt=0;//统计环内个数;
for(int i=1;i<=r;i++)
{
if(!inq[h[i]])
{
int tmp=h[i],u=h[i];
do
{
cir[++cir_cnt]=u;
incir[u]=1;
u=hei[u];
}while(u!=tmp);
break;
}
}
qiumin();
qiumax();
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) hei[i]=read(),rd[hei[i]]++,rudu[hei[i]]++,add(hei[i],i);
for(int i=1;i<=n;i++) if(!vis[i]) solve(i);
printf("%d %d\n",mi,ma);
return 0;
}