Loading

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;
}
posted @ 2021-08-05 19:56  AaMuXiiiiii  阅读(38)  评论(0)    收藏  举报