BC #41
2015-05-18 20:40:13
总结:比赛中搞了两题。。。赛后 A 题被逗比地 FST 了...
后来发现真的是打逗比了... TAT,赛后补到 C 题。
A题 hdu 5228:
题意:给出5张牌,问将其变为同花顺至少需要换多少张牌(同花顺是指花色相同,且连号,注意:10,11,12,13,1 属于连号,但 11,12,13,1,2 不是)
思路:用 vis[][] 标记各个花色是否出现,然后用 n^2 枚举同花顺起点,每个起点开始找5张牌,记录已经出现的牌数 cnt,统计出 cnt 的最大值 cntmax,答案就是 5 - cntmax
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; int T; char s[10]; int vis[4][15]; int main(){ int T; scanf("%d",&T); while(T--){ memset(vis,0,sizeof(vis)); for(int i = 1; i <= 5; ++i){ scanf("%s",s); int a = s[0] - 'A',b = 0; int len = strlen(s); for(int j = 1; j < len; ++j) b = b * 10 + s[j] - '0'; vis[a][b] = 1; } int tmax = 0; for(int i = 0; i < 4; ++i){ for(int j = 1; j <= 9; ++j){ int cnt = 0; for(int k = j; k <= j + 4; ++k) cnt += vis[i][k]; tmax = max(tmax,cnt); } int cnt = 0; for(int k = 10; k <= 13; ++k) cnt += vis[i][k]; cnt += vis[i][1]; tmax = max(tmax,cnt); } printf("%d\n",5 - tmax); } return 0; }
B题 hdu 5229:
题意:有 n 个串,两人博弈,每次等概率选择任意两串,A 先后 B 后手,每次每人能有两种操作:
(1)选一个字符串删掉最后个字符。(2)若两串相同,清空两个字符串。不能操作的人输。问 A 赢的概率。
思路:如果两串的长度和奇数,那么 A 只要保持过程中两串长度不等就必胜(这样一轮两人只能去掉2个,A必取最后个)。当然如果两串本来就相同也是 A 赢。
如果两串长度和为偶数,那么 B 只要保持过程中两串长度不等,就必胜。
所以我们要做的事就很简单了,统计串长度为奇数 / 偶数的串数,以及相等串的数量。然后做简单运算即可,考虑用 map。
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; int T; int N; map<string,int> mp; string s; int Gcd(int a,int b){ while(a > 0 && b > 0) if(a > b) a %= b; else b %= a; return a + b; } int main(){ scanf("%d",&T); while(T--){ mp.clear(); int odd = 0,even = 0,tmp = 0; scanf("%d",&N); for(int i = 1; i <= N; ++i){ cin >> s; mp[s]++; if(s.length() & 1) odd++; else even++; } map<string,int>::iterator it; for(it = mp.begin(); it != mp.end(); it++){ int v = (*it).second; if(v > 1) tmp += v * (v - 1) / 2; } int up = odd * even + tmp; int down = N * (N - 1) / 2; int g = Gcd(up,down); printf("%d/%d\n",up / g,down / g); } return 0; }
C题 hdu 5230:
题意:在1~N-1中若干个不重复的数,使得他们的和在 [L,R] 内。其中 0<=L<=R<N , N<=1e5
思路:首先暴力 dp 可以实现 n^2 的做法,仔细一下,因为数不重复,所以取到几百个数后他们的和就会比1e5大了,所以能取的数并不会很多。
挖掘出这个隐含条件后,dp 的复杂度就降为 n*sqrt(n) 了(类似需要挖掘条件从而缩小范围的题目以前 cf 出过,#286 div2 的 C 题,2014网络赛也见过)
dp 的转移方程比较巧妙,来自题解:设 dp[i][j] 表示当前已经选了最大的 i 个想选的数,和为 j 的方案数。转移时有两种情况:
(1)把之前选择的数全部加1,但是选的数的数量不增加。
(2)把之前选择的数全部加1,再加入新的1,选的数的数量加1。
这种 “位移” 式的统计方法相当巧妙。比赛时只想到了 sqrt(n) 这个点,然而并不会转移。
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const ll mod = 998244353; int T,N,C,L,R; ll dp[2][100010],ans; int main(){ scanf("%d",&T); while(T--){ scanf("%d%d%d%d",&N,&C,&L,&R); L -= C,R -= C; int block = (int)sqrt(2.0 * R); for(int i = 0; i <= R; ++i) dp[0][i] = 0; dp[0][0] = 1; ans = 0; for(int i = 1; i <= block; ++i){ int id = i & 1; for(int j = 0; j <= R; ++j) dp[id][j] = 0; for(int j = i; j <= R; ++j){ dp[id][j] = (dp[id][j] + dp[id][j - i]) % mod; dp[id][j] = (dp[id][j] + dp[id ^ 1][j - i]) % mod; if(j >= L) ans = (ans + dp[id][j]) % mod; } } if(L == 0) ans++; printf("%lld\n",ans); } return 0; }