codeforces #295 div1
2015-03-02 22:19:15
总结:比赛就做了个A,题意比较晦涩,难度中等... 24minAC(速度中等-。-)
QUQ 回到紫名辣,哈哈~
※:比赛中想到了B的解法,死活wa4,赛后改了改TLE,最终把vector换成数组,AC了。
然后,把C题给补了。
A:核心在于考虑到s中的每个字符会与t中的每个字符匹配n次。
统计一下s中A,C,G,T这四个字符每个字符的出现次数c1~c4。
可以发现,如果有个字符出现次数大于其他字符,那么构造的t串肯定都由该字符组成,t仅有1种。
然后进一步,如果出现次数最大的字符有两个,那么构造的t串肯定由这两个字符组成,t有2^n种。
同理,如果出现次数最大的字符有三个,t有3^n种....。如果四个字符出现次数相等,t有4^n种。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <vector> 6 #include <map> 7 #include <set> 8 #include <stack> 9 #include <queue> 10 #include <string> 11 #include <iostream> 12 #include <algorithm> 13 using namespace std; 14 15 #define MEM(a,b) memset(a,b,sizeof(a)) 16 #define REP(i,n) for(int i=1;i<=(n);++i) 17 #define REV(i,n) for(int i=(n);i>=1;--i) 18 #define FOR(i,a,b) for(int i=(a);i<=(b);++i) 19 #define RFOR(i,a,b) for(int i=(a);i>=(b);--i) 20 #define getmid(l,r) ((l) + ((r) - (l)) / 2) 21 #define MP(a,b) make_pair(a,b) 22 23 typedef long long ll; 24 typedef pair<int,int> pii; 25 const int INF = (1 << 30) - 1; 26 const ll MOD = 1e9 + 7; 27 28 int n; 29 char s[100010]; 30 int cnt[10]; 31 32 ll Q_pow(int x,int y){ 33 ll res = 1,X = x; 34 while(y){ 35 if(y & 1) res = (res * X) % MOD; 36 y >>= 1; 37 X = (X * X) % MOD; 38 } 39 return res; 40 } 41 42 bool cmp(int a,int b){ 43 return a > b; 44 } 45 46 int main(){ 47 scanf("%d",&n); 48 scanf("%s",s + 1); 49 for(int i = 1; i <= n; ++i){ 50 if(s[i] == 'A') cnt[1]++; 51 else if(s[i] == 'C') cnt[2]++; 52 else if(s[i] == 'G') cnt[3]++; 53 else cnt[4]++; 54 } 55 sort(cnt + 1,cnt + 4 + 1,cmp); 56 if(cnt[1] > cnt[2]) printf("1\n"); 57 else if(cnt[1] == cnt[2] && cnt[2] > cnt[3]) 58 printf("%I64d\n",Q_pow(2,n)); 59 else if(cnt[1] == cnt[2] && cnt[2] == cnt[3] && cnt[3] > cnt[4]) 60 printf("%I64d\n",Q_pow(3,n)); 61 else printf("%I64d\n",Q_pow(4,n)); 62 return 0; 63 }
B:我承认这题我的代码有点繁琐....
思路的话还是比较清晰的,用一种类似拓扑排序的方法,用队列保存当前可以取的格子。
然后看当前是第一个人取还是第二个人取,如果是第一个人取那么取编号最大的格子,第二个人取编号最小的格子。
上述,我用两个优先队列来实现(这里会涉及到重复取的问题,所以开一个数组标记一下某个编号的格子是否被取掉了。)
上述,用set实现即可,用迭代器遍历的set天然有序,begin()取出最小值,end()的前一个位置取出最大值。
用up[maxn][3],down[maxn][3]来记录格子上方和下方的格子编号。
然后写两个函数,remove用来取掉某个格子,check用来检查格子当前是否可取。
注意:一个坑点在于,当前可以取的格子在以后不一定能取,所以每次从优先队列里top()出来的格子都要check了以后再取。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <vector> 6 #include <map> 7 #include <set> 8 #include <stack> 9 #include <queue> 10 #include <string> 11 #include <iostream> 12 #include <algorithm> 13 using namespace std; 14 15 #define MEM(a,b) memset(a,b,sizeof(a)) 16 #define REP(i,n) for(int i=1;i<=(n);++i) 17 #define MP(a,b) make_pair(a,b) 18 19 typedef long long ll; 20 typedef pair<int,int> pii; 21 const ll mod = 1e9 + 9; 22 23 set<int> st; 24 map< pii,int > mp; 25 int m; 26 int v[100010][2]; 27 int ans[100010],anscnt; 28 int up[100010][3],down[100010][3]; 29 30 bool check(int a){ 31 for(int i = 0; i < 3; ++i) if(up[a][i]){ 32 int id = up[a][i],cnt = 0; 33 for(int j = 0; j < 3; ++j) cnt += !down[id][j]; 34 if(cnt == 2) return false; 35 } 36 return true; 37 } 38 39 void Remove(int a){ 40 int now,id; 41 for(int i = 0; i < 3; ++i){ 42 if(up[a][i]){ 43 id = up[a][i]; 44 for(int j = 2; j >= 0; --j) 45 if(down[id][j] == a) down[id][j] = 0; 46 } 47 if(down[a][i]){ 48 id = down[a][i]; 49 for(int j = 2; j >= 0; --j) 50 if(up[id][j] == a) up[id][j] = 0; 51 } 52 } 53 } 54 55 void Topo(){ 56 for(int i = 1; i <= m; ++i) if(check(i)) st.insert(i); 57 int cur = 1; 58 set<int>::iterator it; 59 while(!st.empty()){ 60 int x; 61 if(cur % 2){ //qmax 62 it = st.end(); 63 it--; 64 x = (*it); 65 st.erase(x); 66 } 67 else{ 68 it = st.begin(); 69 x = (*it); 70 st.erase(x); 71 } 72 if(check(x) == false) continue; 73 cur++; 74 ans[++anscnt] = x; 75 int tem[3] = {down[x][0],down[x][1],down[x][2]}; 76 Remove(x); 77 for(int i = 0; i < 3; ++i) if(tem[i] && check(tem[i])) st.insert(tem[i]); 78 } 79 } 80 81 int main(){ 82 scanf("%d",&m); 83 REP(i,m){ 84 scanf("%d%d",&v[i][0],&v[i][1]); 85 mp[MP(v[i][0],v[i][1])] = i; 86 } 87 REP(i,m){ 88 int id,a = v[i][0],b = v[i][1]; 89 //down 90 if(mp.find(MP(a - 1,b - 1)) != mp.end()) down[i][0] = mp[MP(a - 1,b - 1)]; 91 if(mp.find(MP(a,b - 1)) != mp.end()) down[i][1] = mp[MP(a,b - 1)]; 92 if(mp.find(MP(a + 1,b - 1)) != mp.end()) down[i][2] = mp[MP(a + 1,b - 1)]; 93 //up 94 if(mp.find(MP(a - 1,b + 1)) != mp.end()) up[i][0] = mp[MP(a - 1,b + 1)]; 95 if(mp.find(MP(a,b + 1)) != mp.end()) up[i][1] = mp[MP(a,b + 1)]; 96 if(mp.find(MP(a + 1,b + 1)) != mp.end()) up[i][2] = mp[MP(a + 1,b + 1)]; 97 } 98 Topo(); 99 ll rlt = 0; 100 for(int i = 1; i <= anscnt; ++i) rlt = (rlt * m % mod + ans[i] - 1) % mod; 101 printf("%I64d\n",rlt); 102 return 0; 103 }
C:计算每个数的贡献。
枚举每个数在个位 / 百位/ 千位 / 万位... 依次计算贡献。
为了加速计算,要先求一个前缀和。
那么接下来就比较好搞了。
比如我要求每个数在百位的贡献,我们需要分两种情况:
(1) ans += digit[倒数第二位] * 10^1 * Combination(n - 2,k)
(2) ans += sum[n - i] * 10^1 * Combination(n - 3,k - 1)
之所以分两类,因为第一类计算的是最后一个可能在百位上的数的贡献,第二类计算的是其余的可能在百位上的数的贡献。
如果我们要使某一位数在百位,那么肯定要在这个数后面一个数的后面加一个'+',唯一的特例就是倒数第二数(天然地就是百位)所以分两类。
然后,(1)中Combination(n - 2,k)的意思是最后两个数之间不能插'+',其余n-2个位置间插k个'+'。(2)中Combination(n - 3,k - 1)的意思是首先我要在该位
数后面一个数的后面插'+',然后这两个数之间不能插'+',那么剩下的k-1个'+'插在n-3个位置中。
注意:组合数比较大,要用逆元预处理阶乘,然后计算过程中计算组合数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <vector> 6 #include <map> 7 #include <set> 8 #include <stack> 9 #include <queue> 10 #include <string> 11 #include <iostream> 12 #include <algorithm> 13 using namespace std; 14 15 #define MEM(a,b) memset(a,b,sizeof(a)) 16 #define REP(i,n) for(int i=1;i<=(n);++i) 17 #define REV(i,n) for(int i=(n);i>=1;--i) 18 #define FOR(i,a,b) for(int i=(a);i<=(b);++i) 19 #define RFOR(i,a,b) for(int i=(a);i>=(b);--i) 20 #define getmid(l,r) ((l) + ((r) - (l)) / 2) 21 #define MP(a,b) make_pair(a,b) 22 23 typedef long long ll; 24 typedef pair<int,int> pii; 25 const int INF = (1 << 30) - 1; 26 const ll MOD = 1e9 + 7; 27 28 int n,k; 29 ll sum[100010]; 30 ll ten[100010]; 31 ll fac[100010],afac[100010]; 32 char s[100010]; 33 34 ll Q_pow(int x,int y){ 35 ll res = 1,X = x; 36 while(y){ 37 if(y & 1) res = (res * X) % MOD; 38 X = (X * X) % MOD; 39 y >>= 1; 40 } 41 return res; 42 } 43 44 void Pre(){ 45 fac[0] = afac[0] = 1; 46 REP(i,n){ 47 fac[i] = (fac[i - 1] * (ll)i) % MOD; 48 afac[i] = Q_pow(fac[i],MOD - 2); 49 } 50 ten[0] = 1; 51 REP(i,n) ten[i] = ten[i - 1] * 10LL % MOD; 52 return ; 53 } 54 55 ll Comb(int n,int m){ 56 if(n < m) return 0; 57 return fac[n] * afac[n - m] % MOD * afac[m] % MOD; 58 } 59 60 int main(){ 61 scanf("%d%d",&n,&k); 62 scanf("%s",s + 1); 63 REP(i,n) sum[i] = (sum[i - 1] + s[i] - '0') % MOD; 64 if(k == 0){ 65 ll ans = 0; 66 REP(i,n) ans = (ans * 10LL % MOD + s[i] - '0') % MOD; 67 printf("%I64d\n",ans); 68 return 0; 69 } 70 Pre(); 71 ll ans = 0; 72 REP(i,n){ //位数 73 ans = (ans + (ll)(s[n - i + 1] - '0') * ten[i - 1] % MOD * 74 Comb(n - i,k) % MOD) % MOD; 75 ans = (ans + (ll)sum[n - i] * ten[i - 1] % MOD * 76 Comb(n - i - 1,k - 1) % MOD) % MOD; 77 } 78 printf("%I64d\n",ans); 79 return 0; 80 }