7.20考试总结(NOIP模拟21)[Median·Game·Park]
雨滴降落的速度是每秒十米,我该用怎么样的速度,才能将你挽留?
前言
关于语文素养如何限制OI水平2
,正好现在文化课巨佬们正在考语文(那我走???)
T1 我以为整数是不用输出 .0
的,然后喜挂 30pts。
T2 差值不应该是绝对值???,然后题解认为不需要取,喜挂 45pts。
T1 Median
解题思路
首先明确一下暴力分(线性筛+暴力排序),有 50pts 之高。
其次明确一下中位数需要排序,然后输出方面上面也说了。。
正解有一点玄学,官方题解是这么说的:
然后注意到这题的输入很诡异,其实可以当做是随机数据
那么随机数据满足分布均匀,也就是说可以假定,中位数值变化也是常数级的。
然后题解就认为可以暴力维护中位数指针了。。。(我***)
对于奇数而言,先暴力排序搞出第一个区间的中位数以及各个数的桶,并且记录一下小于等于中位数的数的数量。
然后在向后查找的时候,不断维护更新中位数以及桶还有数量就好了。
偶数的话,维护两个要取平均值的两个数就好了。
Linux编译的时候终端对于大的数据可能会直接判断为 RE,可以开 O2 或者 Ofast:
g++ t.cpp -Ofast -o t && ./t
然后处理的时候注意一下边界问题,并且不要全部开 long long 就 OK 了。
code
#include<bits/stdc++.h>
//#define int long long
#define re long long
#define f() cout<<"Pass"<<endl;
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=1e7+10;
int n,len,mod,num,num1,num2,cnt,pri[N],s[N],s2[N];
int sta[N];
int sum,sum1,sum2,vis[N];
bitset<18*N> jud;
double ans;
void Get_Prime()
{
for(int i=2;cnt<n;i++)
{
if(!jud[i]) pri[++cnt]=i;
for(int j=1;j<=cnt&&pri[j]*i<=18*n;j++){
jud[pri[j]*i]=true;
if(i%pri[j]==0)break;
}
}
}
signed main()
{
n=read();
len=read();
mod=read();
Get_Prime();
for(int i=1;i<=n;i++)
s[i]=1ll*i*pri[i]%mod;
for(int i=1;i<=n;i++)
s2[i]=s[i]+s[i/10+1];
for(int i=1;i<=len;i++)
sta[i]=s2[i],vis[s2[i]]++;
sort(sta+1,sta+len+1);
num=len/2;
if(len&1) ans+=sta[num+1]*1.0;
else ans+=(1.0*sta[num]+1.0*sta[num+1])/2.0;
num=sta[len/2+1];
num1=sta[len/2],num2=sta[len/2+1];
if(len&1)
for(int i=0;i<=num;i++)
sum+=vis[i];
else
{
for(int i=0;i<=num2;i++)
sum1+=vis[i];
for(int i=0;i<=num1;i++)
sum2+=vis[i];
}
for(int l=2;l<=n-len+1;l++)
{
int r=l+len-1;
if(len&1)
{
vis[s2[l-1]]--;
if(s2[l-1]<=num) sum--;
vis[s2[r]]++;
if(s2[r]<=num) sum++;
while(1)
{
if(sum-vis[num]>len/2) sum-=vis[num],num--;
else if(len-sum>len/2) num++,sum+=vis[num];
if(sum-vis[num]<=len/2&&len-sum<=len/2)
break;
}
ans+=num;
}
else
{
vis[s2[l-1]]--;
vis[s2[r]]++;
if(s2[l-1]<=num1) sum1--;
if(s2[r]<=num1) sum1++;
if(s2[l-1]<=num2) sum2--;
if(s2[r]<=num2) sum2++;
while(sum1-vis[num1]>=len/2) sum1-=vis[num1],num1--;
while(sum1+vis[num1+1]<len/2) sum1+=vis[num1+1],num1++;
if(sum1<len/2) num1++,sum1+=vis[num1];
while(sum2-vis[num2]>=len/2+1) sum2-=vis[num2],num2--;
while(sum2+vis[num2+1]<len/2+1) sum2+=vis[num2+1],num2++;
if(sum2<len/2+1) num2++,sum2+=vis[num2];
ans+=(1.0*num1+1.0*num2)/2.0;
}
}
printf("%.1lf",ans);
return 0;
}
T2 Game
解题思路
对于非正解的算法而言,set 或者是优先队列都可以搞到 50pts。
其实并不需要那么麻烦的操作,不难发现,如果要加入的数大于当前区间的最大值的话,那么它就一定会被取走。
因此只需要维护新进入区间的值就可以了。
然后观察到其实值域很小,因此可以开个桶维护。
code
#include<bits/stdc++.h>
#define int long long
#define f() cout<<"Pass"<<endl;
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=1e5+10,m=1e7+10;
int n,opt,now,maxn,ans[2],T,s[N],t[N];
void solve()
{
opt=read();
ans[0]=ans[1]=0;
maxn=now=0;
for(int i=1;i<opt;i++)
{
t[s[i]]++;
maxn=max(maxn,s[i]);
}
for(int i=opt;i<=n;i++)
{
if(s[i]>=maxn) ans[now]+=s[i];
else
{
t[s[i]]++;
t[maxn]--;
ans[now]+=maxn;
while(!t[maxn]) maxn--;
}
now=(!now);
}
for(int i=maxn;i>=1;i--)
while(t[i])
{
ans[now]+=i;
t[i]--;
now=(!now);
}
printf("%lld\n",ans[0]-ans[1]);
}
signed main()
{
n=read();
T=read();
for(int i=1;i<=n;i++)
s[i]=read();
while(T--) solve();
return 0;
}
T3 Park
解题思路
出题人对于[CEOI2017]Chase 的题面魔改了一下就扔给了我们。
树形 DP,开两个数组 f[i][j]
和 g[i][j]
分别记录从 i 到 i 的子树以及从 i 的子树到 i 撒了 j 个的最大贡献。
然后尝试着把在一棵子树中经过子树根节点一条路径(假设是根节点为 rt 从 \(u\rightarrow v\))拆成两部分:
\(u\rightarrow rt\rightarrow v\) 这样就可以用 f 和 g 数组进行转移了。
然后预处理出每个节点周围节点的值的和,在运算的时候减去已经经过的。
最后要注意,对于每一棵子树要正反进行两边,要保证每一种解都可以扫到,毕竟路径的正反是不同的。
code
#include<bits/stdc++.h>
#define int long long
#define f() cout<<"Pass"<<endl;
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=1e5+10;
int n,m,ans,s[N],cnt[N],f[N][110],g[N][110];
int tot,head[N],nxt[N<<1],ver[N<<1];
void add_edge(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs(int x,int fa)
{
vector<int> v;
vector<int>().swap(v);
for(int i=1;i<=m;i++)
{
f[x][i]=cnt[x];
g[x][i]=cnt[x]-s[fa];
}
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(to==fa) continue;
v.push_back(to);
dfs(to,x);
for(int j=0;j<=m;j++)
ans=max(ans,f[x][j]+g[to][m-j]);
for(int j=1;j<=m;j++)
{
f[x][j]=max(f[x][j],max(f[to][j],f[to][j-1]+cnt[x]-s[to]));
g[x][j]=max(g[x][j],max(g[to][j],g[to][j-1]+cnt[x]-s[fa]));
}
}
for(int i=1;i<=m;i++)
{
f[x][i]=cnt[x];
g[x][i]=cnt[x]-s[fa];
}
if(!v.size()) return ;
for(int i=v.size()-1;i>=0;i--)
{
int to=v[i];
for(int j=0;j<=m;j++)
ans=max(ans,f[x][j]+g[to][m-j]);
for(int j=1;j<=m;j++)
{
f[x][j]=max(f[x][j],max(f[to][j],f[to][j-1]+cnt[x]-s[to]));
g[x][j]=max(g[x][j],max(g[to][j],g[to][j-1]+cnt[x]-s[fa]));
}
}
}
signed main()
{
n=read();
m=read();
for(int i=1;i<=n;i++)
s[i]=read();
for(int i=1,x,y;i<n;i++)
{
x=read();
y=read();
add_edge(x,y);
add_edge(y,x);
cnt[x]+=s[y];
cnt[y]+=s[x];
}
dfs(1,0);
printf("%lld",ans);
return 0;
}