【2017.11.06】noip 赛前集训 | T1 洗澡、T2 日记【二分 + 前缀和】

T1  洗澡

洗澡的地方,有一段括号序列,将一个括号修改一次需要1的代价(将左括 号变成右括号或者相反),求最小代价使得括号序列合法

第一次(40)的代码:

  这时候想着把括号分成三个部分:

    ①配对成功的()  这部分直接用栈不停 push / pop 即可。

    ②无法配对的 )   这一部分用 ans 来记录

    ③无法配对的(   就是栈中剩下的部分。

#include <cstdio>
#include <stack>
#include <cstring>
#include <iostream>

int ans;

std::stack<char> st;

std::string s;

int main() {
	freopen("shower.in", "r", stdin);
	freopen("shower.out", "w", stdout);
	
	std::cin >> s;
	
	int len = s.length();
	for (int i = 0; i < len; i++) {
		if (s[i] == '(') st.push(s[i]);
		if (s[i] == ')') {
			if (st.empty()) {
				ans++;
//				st.push('(');
				continue;
			} else {
				st.pop();
			}
		}
	}
	
	ans = ans >= 2 ? ans/2 : ans;
	int ans_ = 0;
	printf("%d\n", ans);
	while (!st.empty()) {
		ans_++;
		st.pop();
	}
	ans_ = ans_ >= 2 ? ans_/2 : ans_;
	
	printf("%d\n", ans + ans_);
	
	return 0;
} 

   本来想关上文件的,打 fclose 打到一半去干别的事.....妥妥的CE。

   看了正解,其实只要把没办法配对的 )看成( 就好了。

#include <cstdio>
#include <stack>
#include <cstring>
#include <iostream>

int ans;

std::stack<char> st;

std::string s;

int main() {
	freopen("shower.in", "r", stdin);
	freopen("shower.out", "w", stdout);
	
	std::cin >> s;
	
	int len = s.length();
	for (int i = 0; i < len; i++) {
		if (s[i] == '(') st.push(s[i]);
		if (s[i] == ')') {
			if (st.empty()) {
				ans++;
				st.push('(');
				continue;
			} else {
				st.pop();
			}
		}
	}
	
//	ans = ans >= 2 ? ans/2 : ans;
	int ans_ = 0;
//	printf("%d\n", ans);
	while (!st.empty()) {
		ans_++;
		st.pop();
	}
	ans_ = ans_ >= 2 ? ans_/2 : ans_;
	
	printf("%d\n", ans + ans_);
	
	return 0;
} 

   其实,不用栈(dalao太神了%%%%)。

#include <cstdio>
#include <cstring>
#include <iostream>

std::string s;

int ans;

int main() {
//	freopen("shower.in", "r", stdin);
//	freopen("shower.out", "w", stdout); 
	
	std::cin >> s;
	
	int len = s.length() - 1, l = 0;
	for (int i = 0; i <= len; i++) {
		if (s[i] == '(') l++;
		if (s[i] == ')') {
			if (l > 0) l--;
			else ans++, l++; 				// 把 )翻转成 (  
		}
	}
	
	printf("%d\n", ans + l / 2);
	
	return 0;
}

 

T2 日记

日记之中,写满了质数,两个质数之间如果没有其他质数,那么则称为相邻的质数。给定𝑁,𝑘,询问不超过𝑁的数中能够表示成连续𝑘个质数之和的最大的数是多少。

下面是我一点也不优美的蜜汁做法,筛素数(O(nloglogn)),没二分,用了前缀和。

测试结果MLE,因为之前在MAXN上乘了二,现在已经改过来了。

#include <cstdio>
#include <ctime>
#include <iostream>
#define ll long long

const int MAXN = 1e7 + 7;

int prime[MAXN], cnt = 1, T, n, k;
ll sum[MAXN];
bool ma[MAXN];

int read() {
	int x = 0;
	char ch = getchar();
	while (ch > '9' || ch < '0') ch = getchar();
	while (ch <= '9' && ch >= '0') x = x * 10 + ch -'0', ch = getchar();
	return x;
}

int main() {
	freopen("diary.in", "r",stdin);
	freopen("diary.out", "w", stdout);
	
//	clock_t start_time=clock();
//预处理 : 
	ma[2] = 1;
	for (int i = 3; i <= MAXN; i++) 
		if (i % 2 == 0) ma[i] = 0; 
		else ma[i] = 1;
	for (ll i = 3; i <= MAXN; i++) 
		if (ma[i]) 
			for (ll j = i * i; j <= MAXN; j += i) 	
				ma[j] = 0;
	
	for(int i = 1; i <= MAXN; i++) 
		if (ma[i]) prime[cnt] = i, sum[cnt] = prime[cnt] + sum[cnt - 1], cnt++;
	
//	for (int i = 1; i <= 1000; i++) printf("%d ", sum[i]);
	
//	printf("%d\n", cnt);
	T = read();
	while (T--) {
		n = read(); k = read();
		if (k == 1) {
			int i = cnt < n / 2 ? cnt - 1 : n / 2;
			while (prime[i] > n) i--;
			printf("%d\n", prime[i]);
		} else {
			int ma_2 = 2;
			int add2 = prime[2] + prime[1];
			while (add2 <= n) {
				ma_2++;							// 小于等于 n 的二数和的最大的那个两个数中大的数的坐标。 
				add2 = prime[ma_2] + prime[ma_2 - 1];
			}
			
			while (sum[ma_2] - sum[ma_2 - k] > n) {
				ma_2--;
			}
			if(ma_2 < k) printf("-1\n");
			else printf("%lld\n", sum[ma_2] - sum[ma_2 - k]);
			                    			   
		}	
	}
	
	
//	clock_t end_time=clock();
//	std::cout<< "Running time is: "<<static_cast<double>(end_time-start_time)/CLOCKS_PER_SEC*1000<<"ms"<<"\n";//输出运行时间	
	
	
	return 0;
}

  现在来研究一下二分的做法。%%%dalao!!!!

#include <cstdio>
#define ll long long

const int MAXN = 1e7 + 7;

int T, cnt = 1;                // 注意 cnt 的初始值
ll prime[MAXN], sum[MAXN];
bool ma[MAXN];

void is_prime() {
	ma[2] = 1;
	for (int i = 3; i <= MAXN; i++) 
		if (i % 2 == 0) ma[i] = 0; 
		else ma[i] = 1;
	for (ll i = 3; i <= MAXN; i++) 
		if (ma[i]) 
			for (ll j = i * i; j <= MAXN; j += i) 	
				ma[j] = 0;
	
	for(int i = 1; i <= MAXN; i++) 
		if (ma[i]) prime[cnt] = i, sum[cnt] = prime[cnt] + sum[cnt - 1], cnt++;

}

ll work(int n, int k) {
	int l = k - 1, r = cnt;
	while (r - l > 1) {
		int mid = l + r >> 1;
		if (sum[mid] - sum[mid - k] <= n) l = mid;
		else r = mid;
	}
	if (l < k) return -1;
	return sum[l] - sum[l - k];
}

int main() {
	freopen("diary.in", "r", stdin);
	freopen("diary.out", "w", stdout);
	
	is_prime();
	
//	for (int i = 1; i <= 100; i++) printf("%d ", prime[i]);
	
	scanf("%d", &T);
	while (T--) {
		int n, k;
		scanf("%d%d", &n, &k);
		printf("%lld\n", work(n, k));
	}
	return 0;
}

 

posted @ 2017-11-06 18:42  E-Valley  阅读(269)  评论(0)    收藏  举报