noip模拟21
A.Median
类似于堆的思想,其实每次移动的时候,只有两个值发生了变化,
每次维护中点值即可,并用数组维护每个值出现的次数,直接枚举权值并更新即可.
之所以这个算法没有被卡掉,那是因为题解里说随机数据满足均匀...
A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define p() printf("Pass")
#define ll long long int
#define ull unsigned ll
#define re register ll
#define lf double
#define mp make_pair
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define fill(x,y) memset(x,y,sizeof x);
#define copy(x,y) memcpy(x,y,sizeof x);
inline void read(ll &ss)
{
ss=0; bool cit=0; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=1;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
if(cit) ss=-ss;
}
inline void write(ll ss)
{
static int stas[35]; int topps=0;
if(ss<0) putchar('-'),ss=-ss;
do{stas[++topps]=ss%10,ss/=10;}while(ss);
while(topps) putchar(stas[topps--]+48); puts("");
}
} using namespace BSS;
const ll N=1e7+50;
bool vis[18*N];
ll n,m,zd,mod,cnt,mid,mid1,mid2,maxn;
ll s1[N],s2[N],pri[N],f[N*2];
inline void Oula()
{
for(re i=2;cnt<=n;i++)
{
if(!vis[i])
{
vis[i]=1;
pri[++cnt]=i;
}
for(re j=1;j<=cnt and i*pri[j]<=18*N;j++)
{
vis[i*pri[j]]=1;
if(i%pri[j]==0) break;
}
}
return ;
}
inline void For_S()
{
for(re i=1;i<=n;i++)
{
s1[i]=(pri[i]*(i%mod))%mod;
s2[i]=s1[i]+s1[i/10+1];
maxn=max(maxn,s2[i]);
}
return ;
}
inline ll up(ll now,ll lim,ll &l)
{
if(l>=lim) return now;
for(ll i=now+1;i<=maxn;i++)
{
l+=f[i];
if(l>=lim) return i;
}
return 0;
}
inline ll down(ll now,ll lim,ll &l)
{
for(ll i=now;i>=0;i--)
{
if(l>=lim and l-f[i]<lim) return i;
l-=f[i];
}
return 0;
}
inline void Work1()
{
zd=(m>>1)+1; lf ans=0; ll l=0;
for(re i=1;i<=m;i++) f[s2[i]]++;
for(re i=0;i<=maxn;i++)
{
l+=f[i];
if(l>=zd and mid==0)
{
ans=ans+(mid=i);
break;
}
}
for(re i=1;i<=n-m;i++)
{
f[s2[i]]--; f[s2[i+m]]++;
if(s2[i]<=mid) l--;
if(s2[i+m]<=mid) l++;
mid=up(mid,zd,l); mid=down(mid,zd,l);
ans=ans+mid;
}
printf("%.1lf",ans);
}
inline void Work2()
{
zd=m>>1;lf ans=0; ll l1=0,l2=0;
for(re i=1;i<=m;i++) f[s2[i]]++;
for(re i=0;i<=maxn;i++)
{
if(!mid1) l1+=f[i];
l2+=f[i];
if(l1>=zd and mid1==0) mid1=i;
if(l2>=zd+1 and mid2==0){ mid2=i; break; }
}
ans=ans+(mid1*1.0+mid2*1.0)/2.0;
for(re i=1;i<=n-m;i++)
{
f[s2[i]]--; f[s2[i+m]]++;
if(s2[i]<=mid1) l1--; if(s2[i+m]<=mid1) l1++;
if(s2[i]<=mid2) l2--; if(s2[i+m]<=mid2) l2++;
mid1=up(mid1,zd,l1); mid1=down(mid1,zd,l1);
mid2=up(mid2,zd+1,l2); mid2=down(mid2,zd+1,l2);
ans=ans+(mid1*1.0+mid2*1.0)/2.0;
}
printf("%0.1lf",ans);
return ;
}
signed main()
{
read(n); read(m); read(mod);
Oula(); For_S();
if(m&1) Work1();
else Work2();
return 0;
}
/*
5998 2995 2995
8939149.0
*/
B. Game
一个单调指针直接扫描.
一个显然的性质:每次放入比序列中最大值更大的权值都会被直接拿走,放入比序列中最大值更小的值,那么原序列的最大值会被拿走,并放入这个稍小值.
所以可以做到\(O(n)\)求解.
比较毒瘤的是这个分数差可以是负的,不可以取绝对值.
B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define pass() printf("Pass")
#define ll int
#define re register ll
#define lf double
#define mp make_pair
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define fill(x,y) memset(x,y,sizeof x);
#define copy(x,y) memcpy(x,y,sizeof x);
inline void read(ll &ss)
{
ss=0; bool cit=0; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=1;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
if(cit) ss=-ss;
}
inline void write(ll ss)
{
static int stas[35]; int topps=0;
if(ss<0) putchar('-'),ss=-ss;
do{stas[++topps]=ss%10,ss/=10;}while(ss);
while(topps) putchar(stas[topps--]+48); puts("");
}
} using namespace BSS;
const ll N=1e5+50;
ll n,m,range,maxn;
ll val[N],f[N];
ll ans1,ans2;
inline ll find(ll now)
{
for(ll i=now;i>=1;i--)
{
if(f[i]) return i;
}
return 0;
}
inline void Work()
{
long long int ans1=0; long long int ans2=0;
for(ll i=1;i<=range;i++)
{
f[val[i]]++;
if(val[i]>maxn) maxn=val[i];
}
ans1+=maxn; f[maxn]--;
while((!f[maxn]) and maxn) maxn--;
ll temp;
for(ll i=1;i<=n-range;i++)
{
temp=range+i;
if(val[temp]>=maxn)
{
if(i&1) ans2+=val[temp];
else ans1+=val[temp];
}
else
{
f[val[temp]]++;
if(i&1) ans2+=maxn;
else ans1+=maxn;
f[maxn]--;
while((!f[maxn]) and maxn) maxn--;
}
}
if((n-range)&1)
{
temp=1;
while(maxn)
{
if(f[maxn])
{
if(temp&1) ans1+=maxn;
else ans2+=maxn;
f[maxn]--; temp++;
}
while((!f[maxn]) and maxn)
{
maxn--;
}
}
}
else
{
temp=1;
while(maxn)
{
if(f[maxn])
{
if(temp&1) ans2+=maxn;
else ans1+=maxn;
f[maxn]--; temp++;
}
while((!f[maxn]) and maxn)
{
maxn--;
}
}
}
printf("%lld\n",ans1-ans2);
}
signed main()
{
read(n); read(m);
for(ll i=1;i<=n;i++)
{
read(val[i]);
}
for(ll ts=1;ts<=m;ts++)
{
read(range); Work();
}
return 0;
}
C. Park
类似于树上求直径,但是个\(dp\).
看到要求权值最大化,应该是想到贪心和\(dp\)的.
考虑使用数据结构维护,但其实根本没有那么复杂..
设计两个\(dp\)数组,\(f_{i,j}\)为从\(i\)走向\(i\)的子树中任意一个点撒了\(j\)的最大收益,\(g_{i,j}\)表示从\(i\)的子树中的节点走到\(i\)撒了\(j\)的最大收益.
考虑如何求出收益.
发现在某个节点\(u\)走向\(v\),无论是否在当前点撒了,都会对答案造成的贡献是所有\(v\)的邻居中的鸽子树\(-u\)中的初始值.
可以\(dfs\)求解,然后在进入下一个节点时更新鸽子数量.
另外要扫两遍,因为对于某个节点来讲,从子树进入节点和从节点进入子树都要计算.
C_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define p() printf("Pass")
#define ll long long int
#define re register ll
#define lf double
#define mp make_pair
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define fill(x,y) memset(x,y,sizeof x);
#define copy(x,y) memset(y,x,sizeof x);
#define pass() printf("Pass")
inline void read(ll &ss)
{
ss=0; bool cit=0; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=1;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
if(cit) ss=-ss;
}
inline void write(ll ss)
{
static int stas[35]; int topps=0;
if(ss<0) putchar('-'),ss=-ss;
do{stas[++topps]=ss%10,ss/=10;}while(ss);
while(topps) putchar(stas[topps--]+48); puts("");
}
} using namespace BSS;
const ll N=1e5+50;
ll n,m,ts,ans;
ll head[N],val[N],alls[N];
ll g[N][150],f[N][150];
ll que[N*2];
struct I { ll u,v,w,nxt; } e[N*2];
inline void add(ll u,ll v)
{
e[++ts].u=u;
e[ts].v=v;
e[ts].nxt=head[u];
head[u]=ts;
}
void dfs(ll now,ll dad)
{
for(re i=1;i<=m;i++)
{
f[now][i]=alls[now];
g[now][i]=alls[now]-val[dad];
}
for(re i=head[now];i;i=e[i].nxt)
{
if(e[i].v==dad) continue;
dfs(e[i].v,now);
for(re j=0;j<=m;j++) ans=max(f[now][j]+g[e[i].v][m-j],ans);
for(re j=1;j<=m;j++)
{
f[now][j]=max(f[now][j],max(f[e[i].v][j],f[e[i].v][j-1]+alls[now]-val[e[i].v]));
g[now][j]=max(g[now][j],max(g[e[i].v][j],g[e[i].v][j-1]+alls[now]-val[dad]));
}
}
for(re i=1;i<=m;i++)
{
f[now][i]=alls[now];
g[now][i]=alls[now]-val[dad];
}
ll top=0,v;
for(re i=head[now];i;i=e[i].nxt)
{
if(e[i].v==dad) continue;
que[++top]=i;
}
while(top)
{
re i=que[top--];
for(re j=0;j<=m;j++) ans=max(f[now][j]+g[e[i].v][m-j],ans);
for(re j=1;j<=m;j++)
{
f[now][j]=max(f[now][j],max(f[e[i].v][j],f[e[i].v][j-1]+alls[now]-val[e[i].v]));
g[now][j]=max(g[now][j],max(g[e[i].v][j],g[e[i].v][j-1]+alls[now]-val[dad]));
}
}
return ;
}
signed main()
{
read(n); read(m);
for(ll i=1;i<=n;i++)
{
read(val[i]);
}
ll u,v,w;
for(ll i=1;i<=n-1;i++)
{
read(u); read(v);
add(u,v); add(v,u);
alls[u]+=val[v];
alls[v]+=val[u];
}
dfs(1,0);
printf("%lld",ans);
return 0;
}

浙公网安备 33010602011771号