考试总结:这次考试,前两道题的题面描述不是很清楚,导致我不知道输出格式到底是什么,挂了差不多80分(好多人也是这样),总体来说,这次考试的前两道题暴力分是打满了,最后一道题打了一个假的暴搜,在考场上没调出来,代码能力还需提高。
T1 Median
思路:我们首先利用线性筛求出我们需要的素数,然后求出整个序列,接下来我们考虑计算中位数,首先明确一个问题,这中位数是将区间里的数从大到小排好序之后的中间的那个数,(我当时因为这个调了老半天),之后我们注意到这个区间长度是一个定值,每次移动只会有两个值出现的次数发生变化,那我们就可以利用一个桶,记录每个数出现的次数,然后维护一个指针指向中位数的位置,当我们移动的时候,相应的更新指针的位置,具体实现见代码:
AC_Code
#include<bits/stdc++.h>
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
const int M=2e7+10;
const int G=1e7+10;
const int P=1e8+8e7;
double ans;
int n,k,w,sum,ls;
int s1[M],s2[M],pr[G],u[G],tong[M];
bool ifs[P];
ii read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=0;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return (f)?x:(-x);
}
iv get_prime()
{
for(re i=2;sum<=G;i++)
{
if(!ifs[i])
{
pr[++sum]=i;
}
for(re j=1;j<=sum&&i*pr[j]<=P;j++)
{
ifs[i*pr[j]]=1;
if(i%pr[j]==0)
break;
}
}
for(re i=1;i<=n;i++)
s1[i]=1ll*pr[i]%w*i%w;
for(re i=1;i<=n;i++)
s2[i]=1ll*s1[i]+s1[i/10+1];
}
int my(int a,int b)
{
return a<b;
}
iv solve1()
{
long long mid;
for(re i=1;i<=k;i++)
{
u[i]=s2[i];
tong[s2[i]]++;
}
sort(u+1,u+k+1,my);
mid=u[(k+1)/2];
ls=upper_bound(u+1,u+k+1,mid)-u-1;
ans+=mid;
for(re i=2;i<=n-k+1;i++)
{
tong[s2[i-1]]--;
tong[s2[i+k-1]]++;
if(s2[i-1]*1ll<=mid)
ls--;
if(s2[i+k-1]*1ll<=mid)
ls++;
while(1)
{
if(1ll*(ls-tong[mid])>=((k+1)/2))
{
ls-=tong[mid];
mid--;
}
else if(1ll*(k-ls)>=((k+1)/2))
{
mid++;
ls+=tong[mid];
}
if((ls-tong[mid]<((k+1)/2))&&(k-ls<((k+1)/2)))
break;
}
ans+=mid;
}
printf("%.1lf\n",(double)ans);
}
iv solve2()
{
double mid;
long long l1,l2,m1,m2;
for(re i=1;i<=k;i++)
{
u[i]=s2[i];
tong[s2[i]]++;
}
sort(u+1,u+k+1,my);
mid=((double)u[k/2]+(double)u[k/2+1])/(double)2;
l1=upper_bound(u+1,u+k+1,u[k/2])-u-1;
l2=upper_bound(u+1,u+k+1,u[k/2+1])-u-1;
m1=u[k/2];
m2=u[k/2+1];
ans+=mid;
for(re i=2;i<=n-k+1;i++)
{
tong[s2[i-1]]--;
tong[s2[i+k-1]]++;
if(s2[i-1]<=m1)
{
l1--;
}
if(s2[i-1]<=m2)
{
l2--;
}
if(s2[i+k-1]<=m1)
{
l1++;
}
if(s2[i+k-1]<=m2)
{
l2++;
}
while(l1-tong[m1]>=(k/2))
{
l1-=tong[m1];
--m1;
}
while(l1+tong[m1+1]<(k/2))
{
++m1;
l1+=tong[m1];
}
if(l1<(k/2))
{
++m1;
l1+=tong[m1];
}
while(l2-tong[m2]>=(k/2+1))
{
l2-=tong[m2];
m2--;
}
while(l2+tong[m2+1]<(k/2+1))
{
++m2;
l2+=tong[m2];
}
if(l2<(k/2+1))
{
++m2;
l2+=tong[m2];
}
ans+=((double)m1+(double)m2)/(double)2;
}
long long out=ans;
if((ans-(double)out)==0.5)
printf("%.1lf\n",(double)out+0.5);
else
printf("%.1lf\n",(double)out);
}
signed main()
{
n=read();
k=read();
w=read();
get_prime();
if(k&1)
solve1();
else
solve2();
/*long long out=ans;
if((ans-(double)out)==0.5)
printf("%.1lf\n",(double)out+0.5);
else
printf("%.1lf\n",(double)out);*/
return 0;
}
T2 Game
思路:因为根据最优策略,如果我要放进去的球比当前序列的最大值还要大,那么我直接就把它拿走了,就不把它放入序列了,所以,如果我们维护一个指向当前序列最大值的指针,那么这个指针显然是单调递减的,我们每次操作后维护这个指针即可,具体实现见代码:
AC_Code
#include<bits/stdc++.h>
#define re register int
#define lc rt<<1
#define rc rt<<1|1
#define mid ((l+r)>>1)
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
const int N=1e5+10;
int n,k,p,cnt,num,mx,use;
int a[N],b[N];
long long tong[N];
long long A,B,la,lb;
ii read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=0;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return (f)?x:(-x);
}
iv change()
{
if(!tong[mx])
{
while(1)
{
mx--;
if(tong[mx]||(mx<=0))
break;
}
}
}
int main()
{
n=read();
k=read();
for(re i=1;i<=n;i++)
a[i]=read();
for(re i=1;i<=k;i++)
{
p=read();
A=0;
B=0;
la=-1;
lb=-1;
cnt=0;
mx=-1;
use=0;
bool pd;
memset(tong,0,sizeof(tong));
for(re j=1;j<=n;j++)
{
while(j<=p&&j<=n)
{
tong[a[j]]++;
mx=max(mx,a[j]);
j++;
}
pd=0;
cnt=j-1;
while(1)
{
if(pd==0||cnt>=n)
{
change();
A+=mx;
tong[mx]--;
change();
}
else if(cnt<n)
{
++cnt;
if(a[cnt]>=mx)
{
A+=a[cnt];
}
else if(tong[mx])
{
A+=mx;
tong[a[cnt]]++;
tong[mx]--;
change();
}
else
{
tong[a[cnt]]++;
change();
A+=mx;
tong[mx]--;
change();
}
}
++use;
if(use==n)
break;
if(cnt<n)
{
++cnt;
if(a[cnt]>=mx)
{
B+=a[cnt];
}
else if(tong[mx])
{
B+=mx;
tong[a[cnt]]++;
tong[mx]--;
change();
}
else
{
tong[a[cnt]]++;
change();
B+=mx;
change();
}
}
else
{
change();
B+=mx;
tong[mx]--;
change();
}
++use;
if(use==n)
break;
if(la==A&&lb==B)
break;
la=A;
lb=B;
pd=1;
}
break;
}
printf("%lld\n",A-B);
}
return 0;
}
T3 Park
一道DP好题,思路:设\(dp_{0,i,j}\)表示我从 i 的子树走到i,一共撒了 j 次的最大差值,设 \(dp_{1,i,j}\)表示从 i的father
走向 i ,一共撒了 j 次的最大差值,那么状态转移方程可得 \(dp_{0,i,j}=max(dp_{0,i,j},max(dp_{0,son,j},dp_{0,son,j-1}+sum_i-p_{son}))\) ,\(dp_{1,i,j}=max(dp_{1,i,j},max(dp_{1,son,j},dp_{1,son,j-1}+sum_i-p_{father}))\)
但是,因为我们自上而下和自下而上计算可能会得到不一样的结果,所以我们还要反着做一遍DP
代码如下:
AC_Code
#include<bits/stdc++.h>
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
const int N=1e5+10;
int n,v;
long long ans;
long long p[N],dp[2][N][110],sum[N];
vector<int> edge[N];
ii read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=0;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return (f)?x:(-x);
}
iv dfs(int x,int f)
{
for(re i=1;i<=v;i++)
{
dp[0][x][i]=sum[x];
dp[1][x][i]=sum[x]-p[f];
}
for(re i=0;i<edge[x].size();i++)
{
if(edge[x][i]==f)
continue;
dfs(edge[x][i],x);
for(re j=1;j<v;j++)
ans=max(ans,dp[0][x][j]+dp[1][edge[x][i]][v-j]);
for(re j=1;j<=v;j++)
{
dp[0][x][j]=max(dp[0][x][j],max(dp[0][edge[x][i]][j],dp[0][edge[x][i]][j-1]+sum[x]-p[edge[x][i]]));
dp[1][x][j]=max(dp[1][x][j],max(dp[1][edge[x][i]][j],dp[1][edge[x][i]][j-1]+sum[x]-p[f]));
}
}
reverse(edge[x].begin(),edge[x].end());
for(re i=1;i<=v;i++)
{
dp[0][x][i]=sum[x];
dp[1][x][i]=sum[x]-p[f];
}
for(re i=0;i<edge[x].size();i++)
{
if(edge[x][i]==f)
continue;
for(re j=1;j<v;j++)
ans=max(ans,dp[0][x][j]+dp[1][edge[x][i]][v-j]);
for(re j=1;j<=v;j++)
{
dp[0][x][j]=max(dp[0][x][j],max(dp[0][edge[x][i]][j],dp[0][edge[x][i]][j-1]+sum[x]-p[edge[x][i]]));
dp[1][x][j]=max(dp[1][x][j],max(dp[1][edge[x][i]][j],dp[1][edge[x][i]][j-1]+sum[x]-p[f]));
}
}
for(re i=1;i<=v;i++)
ans=max(ans,max(dp[0][x][i],dp[1][x][i]));
}
signed main()
{
n=read();
v=read();
for(re i=1;i<=n;i++)
scanf("%lld",&p[i]);
int a,b;
for(re i=1;i<n;i++)
{
a=read();
b=read();
edge[a].push_back(b);
edge[b].push_back(a);
sum[a]+=p[b];
sum[b]+=p[a];
}
dfs(1,0);
printf("%lld\n",ans);
return 0;
}