noip模擬4

ccjjxx發表於2024-11-03

A Median

打了 \(70\) 分,但是因為 \(printf("%.1lf")\) 慘遭爆零。

原因詳見我寫的討論。

首先,你需要把 \([1,10^7]\) f範圍是質數全部篩出來,大概耗時半秒。

然後,考慮資料是根據質數構造的,所以近似為隨機。

那麼既然隨機,那咱們直接對於每次移動去維護中位數的位置就好了。

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1.1e7+7,M=5e5+5,K=180000000;
//180000000
bitset<K>vis;
bitset<K>isp,p1;
int p[N];
int ccnt;
int ans;
inline void init(int n)
{
	for(int i=2;i<=n;i++)
	{
		if(!vis[i]) p[++ccnt]=i;
		for(int j=1;1ll*i*p[j]<=n;j++)
		{
			vis[i*p[j]]=1;
			if(i%p[j]==0) break;
		}
	}
}
int n,k,w;
int s[N],s2[N];
int sub[N];
const int mx=1e7;
int cnt[N];
namespace sub1{
	inline void init2(int n)
	{
		for(int i=2;i<=n;i++)
		{
			if(!isp[i])
			{
				p1[++ccnt]=(i%3==2?1:0);
				for(int j=2;j*i<=n;j++) isp[j*i]=1;
			}
		}
	}
	int c[6];
	void Q()
	{
		init2(180000000);
		p1[2]=0,p1[1]=2,s2[0]=5;
		for(int i=1;i<=n;i++)
		{
			if(i==2)
			{
				s[i]=0,s2[i]=s[i/10+1];
				continue;
			}
			s[i]=((p1[i]==1?2:1)*i)%w;
			s2[i]=s[i]+s[i/10+1];
		}
		for(int i=0;i<k;i++)c[s2[i]]++;
		double ans=0;
		for(int i=1;i<=n-k+1;i++)
		{
			c[s2[i-1]]--,c[s2[i+k-1]]++;
			if(k%2==1)
			{
				int mid=(k+1)/2,lcnt=0,j=0;
				for(;j<=4;j++)
				{
					lcnt+=c[j];
					if(lcnt>=mid) break;
				}
				ans+=j;
			}
			else
			{
				int mid=k>>1,lcnt=0,j=0;
				double res=0;
				for(;j<=4;j++)
				{
					lcnt+=c[j];
					if(lcnt>mid) 
					{
						res=j;break;
					}
					else if(lcnt==mid)
					{
						int k=j+1;
						while(!c[k])k++;
						res+=(double)(j+k)/2.0;
						break;
					}
				}
				ans+=res;
			}
		}
		printf("%.1f",ans);
		exit(0);
	}
}
signed main()
{
	freopen("median.in","r",stdin);
	freopen("median.out","w",stdout);
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n>>k>>w;
	if(w==3)sub1::Q();
	init(180000000);
	for(int i=1;i<=n;i++) 
	s[i]=(p[i]*i%w),s2[i]=s[i]+s[i/10+1];
	for(int i=1;i<=k;i++) cnt[s2[i]]++;
	if(k&1)
	{
		int sum=0,tp1=0,tp2=0,cnt1=0,cnt2=0;
		for(int i=1;i<=mx;i++)
		{
			sum+=cnt[i];
			if(sum>=k/2+1)
			{
				cnt1=sum-cnt[i],cnt2=k-sum;
				tp1=tp2=i;
				break;
			}
			if(cnt[i]) tp1=i;
		}
		ans+=tp1+tp2;
		int num=k/2;
		for(int i=1;i<=n-k;i++)
		{
			int lst=s2[i],now=s2[i+k];
			cnt[lst]--,cnt[now]++;
			cnt1+=-(lst<tp1)+(now<tp1);
			cnt2+=-(lst>tp1)+(now>tp1);
			while(cnt1>num)
				cnt2+=cnt[tp1],tp1--,cnt1-=cnt[tp1];
			while(cnt2>num)
				cnt1+=cnt[tp1],tp1++,cnt2-=cnt[tp1];
			ans+=tp1*2;
		}
	}
	else
	{
		int num=k/2,cnt1=0,cnt2=0;
		int tp1=-1,tp2=-1;
		for(int i=k;i<=n;i++)
		{
			if(i!=k) cnt[s2[i]]++;
			if(s2[i]<=tp1) cnt1++;
			if(s2[i]<=tp2) cnt2++;
			if(i>k)
			{
				cnt[s2[i-k]]--;
				if(s2[i-k]<=tp1) cnt1--;
				if(s2[i-k]<=tp2) cnt2--;
			}
			while(cnt1<num) cnt1+=cnt[++tp1];
			while(cnt2<=num) cnt2+=cnt[++tp2];
			while(cnt1>=num+cnt[tp1]) cnt1-=cnt[tp1--];
			while(cnt2>=num+1+cnt[tp2]) cnt2-=cnt[tp2--];
			ans+=tp1+tp2;
		}
	}
	if(ans&1) cout<<ans/2<<".5";
	else cout<<ans/2<<".0";
}

B Game

太可惡了。考場上差點打出來,結果忽然不想打了,痛失 50。

\(\log\) 做法是顯然的。拿優先佇列每次取出隊頭久好了。

但是 \(O(nk\log n)\) 不能接受啊。

那其實我們可以從值域下手。

維護最大值,每次暴力地看最大值是否被修改,如果修改,則更新最大值。

這時候最大值只可能是被取走,因為如果當前值比最大值還要大,那麼你會優先取走這個。

因此,序列中維護的最大值是單調不增的。

那我們維護一次就是 \(O(n)\),總複雜度 \(O(nk)\)

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k;
const int N=1e6+6;
int a[N], t[N];
signed main()
{
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>a[i];
	while(k--)
	{
		int cnta=0,cntb=0;
		int p;cin>>p;
		int mx=0;
		for(int i=1;i<p;i++) t[a[i]]++,mx=max(mx,a[i]);
		int stp=0;
		for(int i=p;i<=n;i++)
		{
			stp++;
			if(a[i]>=mx) stp%2==1?cnta+=a[i]:cntb+=a[i];
			else
			{
				t[a[i]]++,t[mx]--;
				(stp)%2==1?cnta+=mx:cntb+=mx;
				while(!t[mx])--mx;
			}
		}
		for(int i=mx;i>=1;i--)
		{
			while(t[i])
			{
				(++stp)%2==1?cnta+=i:cntb+=i;
				t[i]--;
			}    
		}
		cout<<cnta-cntb<<"\n";
	}
	return 0;
}

C Park

樹形 dp,我不會。

點選檢視程式碼
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	register char ch=getchar();register int x=0;
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
const int N=1e5+5,V=105;
int n,v,ans,a[N],f[N][V][2],g[N][V][2];
vector<int>G[N];
void dfs(int x,int p){
	f[x][0][1]=g[x][0][1]=-1e16;
	int sum=0;
	for(int i=0;i<G[x].size();i++){
		int y=G[x][i];
		if(y==p)continue;
		dfs(y,x);
		sum+=a[y];
	}
	g[x][1][1]=sum+a[p];
	for(int i=0;i<G[x].size();i++){
		int y=G[x][i];
		if(y==p)continue;
		int mxf=-1e16,mxg=-1e16;
		for(int j=v;j>=0;j--){
			mxf=max(mxf,max(f[x][v-j][0],f[x][v-j][1]+a[p]-a[y])),
			mxg=max(mxg,max(g[x][v-j][0],g[x][v-j][1]));
			ans=max(ans,mxg+max(f[y][j][0],f[y][j][1]));
			ans=max(ans,mxf+max(g[y][j][0],g[y][j][1]));
		}
		for(int j=1;j<=v;j++){
			f[x][j][0]=max(f[x][j][0],max(f[y][j][0],f[y][j][1])),
			g[x][j][0]=max(g[x][j][0],max(g[y][j][0],g[y][j][1]));
			f[x][j][1]=max(f[x][j][1],max(f[y][j-1][0],f[y][j-1][1])+sum),
			g[x][j][1]=max(g[x][j][1],max(g[y][j-1][0],g[y][j-1][1])+sum-a[y]+a[p]);
		}
	}
}
signed main(){
	freopen("park.in","r",stdin);
	freopen("park.out","w",stdout);
	n=read(),v=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1,u,v;i<n;i++){
		u=read(),v=read();
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(1,0);
	cout<<ans;
}

D 路徑

變形金剛。

image

我只會 \(35\)\(O(n^2 \log n)\) 暴力和 \(k=1\) 的 dp。。。