一些NOI.AC模擬賽的題

1.小奇挖礦 (一道調了n久的線段樹)

難點:如何把問題轉化成幾個可以用線段樹修改的問題。

於是我們就有了幾個函數:update, 區間求和,單點求和,找最後一個某個數i在第幾個位置。

逐一實現即可

較難的就是最後一個操作,我們需要知道min,max才能做到,運用二分的思想。若是一個以x爲根的子樹裏最大的數都小於要求的樹,那就捨棄這個子樹。若是最小值比這個數要小並最大值比這個數要大,那我們就繼續排查這個子樹。

大致程序就是這樣的

inline int query_num(int root,int l,int r,int x)//第幾大 
{
	if(mn[root]>=x) return r-l+1;
	if(mx[root]<x) return 0;
	int mid=(l+r)>>1;push_down(root,l,r);
	return query_num(root<<1,l,mid,x)+query_num(root<<1|1,mid+1,r,x);
}

2.一分爲二(一道有趣的我調了2個小時的dp題)

並不是非常難,先排序(這樣我們就可以知道兩個數的大小關係了),然後dp[i][j]表示前i個裏面第一組選了j個。難點是轉移。我們不妨打破一些東西,考慮每個元素對答案的貢獻(正還是負)然後就好啦啦啦

還有就是dp方程儘量正着推,這樣好搞

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn=3005;
int n,m,a[maxn];ll f[maxn][maxn]; 
int main()
{
//	freopen("split1.in","r",stdin);
	scanf("%d%d",&n,&m);
	if(m>n-m) m=n-m;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	sort(a+1,a+1+n);
	memset(f,0x3f,sizeof(f));
	//直接考慮單個a的貢獻 
	f[0][0]=0;
	for(int i=0;i<=n;i++)
	{
		for(int j=0;j<=min(i,m);j++)
		{
			f[i+1][j]=min(f[i][j]+1ll*a[i+1]*(2*j-m),f[i+1][j]);
			f[i+1][j+1]=min(f[i+1][j+1],f[i][j]+1ll*((i-j)-(n-m-i+j))*a[i+1]);
		}	
	}
	printf("%lld",f[n][m]);
	return 0;
} 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章