1. 程式人生 > 實用技巧 >NOIP2015普及組

NOIP2015普及組

T1

金幣

很簡單的題,控制天數這個變數

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=100010;
const int INF=0x3fffffff;
int x;
int main(){
	cin>>x;
	int ans=0,b=0;
	for(int i=1;i<=x;i++){
		for(int j=1;j<=i;j++){
			ans+=i;
			b++;
			if(b>=x) break;
		}
		if(b>=x) break;
	}
	cout<<ans<<endl;
return 0;
}

  

T3 求和

這道題還是比較好的

涉及公式推理,推理很重要!!!!

https://blog.csdn.net/ThinFatty/article/details/53054377?locationNum=4&fps=1

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
//欸這種題我就只想得到暴力。。。。
//但是很明顯不行的。。。仔細思考應該需要找規律,推公式。。。 簡化 
//很好的一道題 
long long num[100001],col[10001];
struct node{
	long long ab,a,b,op;
	//op是出現次數,ab是預處理的sum(編號*數字),a是預處理的sum數字,b是預處理的sum標號 
}d[10001][2];
int n,m;
int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld",&num[i]);
	for(int i=1;i<=n;i++) {
		scanf("%lld",&col[i]);
		d[col[i]][i%2].op++;
		d[col[i]][i%2].ab+=i*num[i];
		d[col[i]][i%2].a+=num[i];
		d[col[i]][i%2].b+=i;
		d[col[i]][i%2].ab%=10007;
		d[col[i]][i%2].a%=10007;
		d[col[i]][i%2].b%=10007;
	};
	long long ans=0;
	for(int i=1;i<=m;i++){
		for(int j=0;j<=1;j++){
			if(d[i][j].op<2) continue;
			ans+=(d[i][j].op-2)*d[i][j].ab;
			ans%=10007;
			ans+=d[i][j].a*d[i][j].b;
			ans%=10007;
		}
	}
	printf("%lld\n",ans);
return 0;
}

  

T4 推銷員

先來分析一下題目。從題目中的樣例,我們可以得到一個猜想:後面的決策一定包含前面的決策。這個結論是可以證明的,證明過程這裡就不贅述了。因此,我們只需要分階段一步步在決策中新增住戶即可。對於某一個決策,我們設離入口最遠的住戶編號是x,編號為i的住戶離入口的距離是s[i],新增的疲勞值是a[i],則要新增住戶無非就是兩種情況:一是在最遠住戶之前找一個住戶新增入決策中,這樣新累積的疲勞值是a[i];二是在最遠住戶之後找一個住戶新增入決策中,這樣新累積的疲勞值是a[i]+s[i]*2-s[x]*2。對於這兩種情況分別找出新增的疲勞值的最大值,然後再進行選擇即可。可是,用直接的方法找最大值是O(n)的,這樣使得整個程式是O(n^2),又因為n可達100000,因此這個方案不可行。此時我們就可以採用優先佇列來處理,將找最大值的複雜度減小到O(logn)。用兩個優先佇列Q1,Q2分別表示最遠住戶前面的住戶所新增的疲勞值組成的佇列和最遠使用者後面的住戶所新增的疲勞值組成的佇列,其中要注意的是,Q1中第i住戶所對應的元素是a[i],而在Q2中第i住戶所對應的元素是a[i]+s[i]*2。然後,對於每次決策,分別取Q1和Q2的頂端元素,比較Q1的頂元素和Q2的頂元素-s[x]*2(相當於比較a[i]和a[j]+s[j]*2-s[x]*2,即兩個住戶所新新增的疲勞值),如果選擇最遠住戶前面的住戶,則將答案累加後直接pop即可,如果選擇最遠住戶後面的住戶,就要注意將x後移,並將新的已成為最遠住戶前面住戶的元素加入Q1中。在操作過程中,我們用一個v陣列標記該住戶是否被選過,以便在提取最大值的時候不出現重複。

根據題目的資料範圍,也能知道不能暴力或者dp

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=100010;
const int INF=0x3fffffff;
//這道題的資料規模明顯說明了不能用暴力或者dp
//於是思考,怎樣對下一步進行選擇,因為這個是一步一步的選擇下去的,後面的包含前面的,每次都在還沒有走過的需要消耗最多能力的那個選擇
//那麼怎麼選擇,可以分為從當前位置向前走或者是向後走,那麼就有兩種選擇,
//向前走,不用走多餘的路,那麼疲憊值就是a[i]
//向後走,需要走多餘的路,那麼疲憊值就是a[i]+2*s[i] (當然需要剪掉)
//那麼為了提高效率,設定兩個優先佇列,分別儲存前面的和後面的疲憊之最大的
typedef pair<int,int> pa;
priority_queue<pa> q1,q2;
//priority_queue<pa> temp1,temp2;
//用一個v陣列標記該住戶是否被選過,以便在提取最大值的時候不出現重複
int n;
bool  vis[maxn]={0};
int dis[maxn],tri[maxn];
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d",&dis[i]);
	for(int i=1;i<=n;i++) {
		scanf("%d",&tri[i]);
		q2.push(make_pair(tri[i]+2*dis[i],i));  //q2在這裡放進去 
	}
	int ans=0;
	int index=0; //這個是現在到的那個地方 
	for(int i=1;i<=n;i++){
		while(!q1.empty()&&vis[q1.top().second]) q1.pop(); //把訪問過的都彈出去 
		while(!q2.empty()&&(vis[q2.top().second]||q2.top().second<index)) q2.pop();//訪問過的並且已經小於當前的了就彈出去
		int t1=-1,t2=-1;
		if(!q1.empty()) t1=q1.top().first;
		if(!q2.empty()) t2=q2.top().first-2*dis[index]; //兩個疲憊值
		if(t1>t2){ //選前面的,那麼就要q1彈出去 
			vis[q1.top().second]=1;
			ans+=t1;
			q1.pop();
		} 
		else{
			vis[q2.top().second]=1;
			ans+=t2;
			while(index<q2.top().second){ //q1在這裡放進去,現在位置之前的 
			 q1.push(make_pair(tri[index],index));
			 index++;
			}
			q2.pop();
		}
		printf("%d\n",ans);
		/*
		temp1=q1;
		while(!temp1.empty()){
			cout<<temp1.top().second<<" ";
			temp1.pop();
		}
		cout<<endl;
		temp2=q2;
		while(!temp2.empty()){
			cout<<temp2.top().second<<" ";
			temp2.pop();
		}
		cout<<endl;
		*/
	}
	
	
return 0;
}