T1 密码锁
对于一种状态,其可能产生的其他状态共有两种情况,一种情况是只转一个拨圈,这样产生的密码共有5∗9=455*9=455∗9=45种,另一种情况是转相邻的两个拨圈,这样产生的密码共有4∗9=364*9=364∗9=36种,所以一种状态能产生的密码共有45+36=8145+36=8145+36=81种。现在题目中给出了n(n≤8)n(n\leq 8)n(n≤8)个状态,问能满足所有这些状态的密码有多少种。
由于密码只有555位,所以总共的情况也只有10510^5105种,对于每种密码,我们只需要判断它是不是能到达给出的nnn个状态。判断时,我们遍历nnn个状态,对每个状态分别进行转一个拨圈的变换和转两个拨圈的变换,看是否能够得到该密码,若nnn个状态都符合条件,则答案加一。复杂度是完全可以接受的。
再讲一种复杂度较小的做法。对于每种状态(n≤8)(n\leq 8)(n≤8),枚举这个状态所能到达的所有密码(818181种),记录每种密码被枚举到的次数。枚举完nnn个状态能到达的密码后,遍历所有密码,若这个密码被枚举到的次数为nnn,也就说明每个状态都能到达这个密码,则答案加一。
#include <bits/stdc++.h>
using namespace std;
int n, m[10][6], ans;
map<string, int> mp;
int main(int argc, char const *argv[]) {
	cin >> n;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= 5; j++)
			cin >> m[i][j];
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= 5; j++) {
			for (int k = 0; k < 10; k++) {
				string s = "";
				for (int l = 1; l < j; l++) s += char(m[i][l] + '0');
				if (m[i][j] == k) continue;
				s += char(k + '0');
				for (int l = j + 1; l <= 5; l++) s += char(m[i][l] + '0');
				mp[s]++;
			}
			if (j == 5) continue;
			for (int k = 1; k < 10; k++) {
				string s = "";
				for (int l = 1; l < j; l++) s += char(m[i][l] + '0');
				s += char((m[i][j] + k + 10) % 10 + '0');
				s += char((m[i][j + 1] + k + 10) % 10 + '0');
				for (int l = j + 2; l <= 5; l++) s += char(m[i][l] + '0');
				mp[s]++;
			}
		}
	for (auto i : mp)
		if (i.second == n)
			ans++;
	cout << ans << endl;
}
T2 消消乐
这个题的重点在于如何判断一个串是否可以被消除:用一个栈依次读串里的字符,若该字符与栈顶的元素相同,则弹出栈顶,否则把该元素放入栈,最后看栈是否为空即可,这样就得到了n3n^3n3的做法,有353535分。
// 35pts
#include <bits/stdc++.h>
#define A 100010
using namespace std;
int n, top, ans; string s;
char sta[A];
int main(int argc, char const *argv[]) {
	cin >> n >> s;
	for (int l = 0; l < n; l++)
		for (int r = l + 1; r < n; r++) {
			top = 0;
			for (int i = l; i <= r; i++) {
				if (sta[top] == s[i]) top--;
				else sta[++top] = s[i];
			}
			if (top == 0) ans++;
		}
	cout << ans << endl;
}
在上面的做法中,对于相同的左端点,我们枚举了所有的右端点,重复判断了许多次,其实只要从左端点开始走一遍到末尾就可以,每走一步都判断一下栈是否为空。复杂度降为n2n^2n2,有505050分。
// 50pts
#include <bits/stdc++.h>
#define A 200010
using namespace std;
int n, top, ans; string s;
char sta[A];
int main(int argc, char const *argv[]) {
	cin >> n >> s;
	for (int l = 0; l < n; l++) {
		top = 0;
		for (int r = l; r < n; r++) {
			if (sta[top] == s[r]) top--;
			else sta[++top] = s[r];
			if (top == 0) ans++;
		}
	}
	cout << ans << endl;
}
再看下一个部分分,“字符串中的每个字符独立等概率地从字符集中选择”,也就是说我们确定了左端点之后,在右端点不断增大的过程中,这个串会越来越长,能产生消除串的概率会越来越低,所以给右端点定一个界限,我们认为在这个界限之后就不可能产生消除串。这个界限根据左端点的个数来定,该部分分的数据范围下n≤2∗105n\leq2*10^5n≤2∗105,那就卡着能跑过的数据范围,把右端点的界限设置到100010001000(2∗1082∗105\frac{2*10^8}{2*10^5}2∗1052∗108=1000),这样能拿到606060分。
// 60pts
#include <bits/stdc++.h>
#define A 200010
using namespace std;
int n, top, ans; string s;
char sta[A];
int main(int argc, char const *argv[]) {
	cin >> n >> s;
	for (int l = 0; l < n; l++) {
		top = 0;
		int cnt = 1000;
		for (int r = l; r < n; r++) {
			if (sta[top] == s[r]) top--;
			else sta[++top] = s[r];
			if (top == 0) ans++;
			if (--cnt < 0 and n > 8000) break;
		}
	}
	cout << ans << endl;
}
其实能产生一个子串的条件是,这两个栈的状态是相同的。例如样例“accabccb”,初始时为空栈,代表的位置为000;“acca”为空栈,代表的位置为333;“accabccb”为空栈,代表的位置为777;这三个位置中每两个位置的组合都可以产生一个串,也就是C32C^2_3C32,共可以产生333个串。所以我们统计每种栈的状态出现的次数nnn,答案累计上Cn2=n(n−1)2C^2_n=\frac{n(n-1)}{2}Cn2=2n(n−1)即可。这样可以拿到909090分,会有两个点MLEMLEMLE,就是刚才50−6050-6050−60分的两个点,原因是栈的状态太多,可能会产生大约nnn个字符串,串的长度从111递增到nnn,mapmapmap占的空间太大。
// 90pts
#include <bits/stdc++.h>
#define A 2000010
using namespace std;
typedef long long ll;
int n, top;
string s, now;
char sta[A];
ll ans;
map<string, int> mp;
int main(int argc, char const *argv[]) {
	cin >> n >> s;
	mp[""]++;
	for (int i = 0; i < n; i++) {
		if (sta[top] == s[i]) top--, now.pop_back();
		else sta[++top] = s[i], now += s[i];
		mp[now]++;
	}
	for (auto i : mp)
		ans += 1ll * (i.second * 1ll * (i.second - 1ll) / 2);
	cout << ans << endl;
}
到这里可以想到把上面两个代码合一下就可以满分,所以现在问题来到了如何特判11、1211、1211、12这两个点。这两个点的条件上面也分析过了,“字符串中的每个字符独立等概率地从字符集中选择”,a−za-za−z这262626个字母出现的概率是相同的,那我们就在输入字符串之后统计一下每个字母出现的次数,如果该字母出现的次数与期望次数的差值过大,那么我们认为这个点不是第111111个点或第121212个点。
// 100pts
#include <bits/stdc++.h>
#define A 2000010
using namespace std;
typedef long long ll;
int n, top, t[27];
string s, now;
char sta[A];
ll ans;
bool flag;
map<string, int> mp;
int main(int argc, char const *argv[]) {
	cin >> n >> s;
	for (int i = 0; i < n; i++) t[s[i] - 'a']++;
	for (int i = 0; i < 26; i++) // 判断测试点
		if (abs(t[i] - n / 26) > 10000) {
			flag = 1;
			break;
		}
	if (flag) {
		mp[""]++;
		for (int i = 0; i < n; i++) {
			if (sta[top] == s[i]) top--, now.pop_back();
			else sta[++top] = s[i], now += s[i];
			mp[now]++;
		}
		for (auto i : mp)
			ans += 1ll * (i.second * 1ll * (i.second - 1ll) / 2);
		cout << ans << endl;
	}
	else {
		for (int l = 0; l < n; l++) {
			top = 0;
			int cnt = 1000;
			for (int r = l; r < n; r++) {
				if (sta[top] == s[r]) top--;
				else sta[++top] = s[r];
				if (top == 0) ans++;
				if (--cnt < 0 and n > 8000) break;
			}
		}
		cout << ans << endl;
	}
}
 
                   
                   
                   
                   
       
           
                 
                 
                 
                 
                 
                
               
                 
                 
                 
                 
                
               
                 
                 扫一扫
扫一扫
                     
                     
              
             
                   2519
					2519
					
 被折叠的  条评论
		 为什么被折叠?
被折叠的  条评论
		 为什么被折叠?
		 
		  到【灌水乐园】发言
到【灌水乐园】发言                                
		 
		 
    
   
    
   
             
					 
					 
					


 
            