这场CF还是比较简单的吧.....要是作比赛的时候心情好一点还是能做的蛮好的
Problem 451A. Game With Sticks
题目大意:
就是现在又m条竖线和n条横线,他们相交形成n*m个交点,现在两个人依次选择,每个人选择一个交点,那么经过这个点的所有直线都将消失,当最后轮到谁选的时候如果没有交点可选了,那个人就输了,要求给了n和m之后输出结果
大致思路:
首先要想到每次选取一个点之后,n 和 m 都相当于减少1,剩下(n - 1)*(m - 1) 个交点
那么当n和m在选取k次之后就会变成 n - k, m - k,这样很容易发现当n或者m当中的一个降为0的时候就会不再有交点
那么这个游戏的结果就之后n和m中较小的那一个有关,判断较小的那一个数的奇偶性即可
代码如下:
Result : Accepted Memory : 4 KB Time : 30 ms
/* * Author: Gatevin * Created Time: 2014/7/24 23:28:16 * File Name: test.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; int n,m; int main() { cin>>n>>m; m = min(n,m); if(m&1) { cout<<"Akshat"<<endl; } else { cout<<"Malvika"<<endl; } return 0; }
Problem 451B . Sort The Array
题目大意:
就是现在给出一个数组a[n],问是否能够将其中的一整段反过来摆放使得得到的数列单调递增,如果不能输出"no“,能就输出”yes“然后输出要反向的区间段的起始和终点
大致思路:
首先如果原数列本身就是单调递增的话,直接反向1,1即可
如果不是就先将数列按升序排序然后与原数列进行对比,从第一个开始对比找到第一个不相同的位tmp1,然后从最后一位开始向前对比找到不相同的第一位tmp2,
那么就是对比排列好的数列和原数列的 a [ tmp1 ~ tmp2 ] 之间是不是正好是反的关系即可
如果是就输出"yes" tmp1, tmp2,不是就输出 ” no"
代码如下:
Result : Accepted Memory : 796 KB Time : 93 ms
/* * Author: Gatevin * Created Time: 2014/7/24 23:37:21 * File Name: test.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; int n; int a[100010]; int b[100010]; int main() { cin>>n; for(int i = 1; i <= n; i++) { cin>>a[i]; b[i] = a[i]; } sort(b + 1, b + n + 1); bool flag = true; int tmp1, tmp2; for(int i = 1; i <= n; i++) { if(a[i] != b[i]) { flag = false; tmp1 = i; break; } } for(int i = n; i >= 1; i--) { if(a[i] != b[i]) { flag = false; tmp2 = i; break; } } if(flag) { cout<<"yes"<<endl; cout<<"1 1"<<endl; return 0; } flag = true; for(int i = 0; i < tmp2 - tmp1; i++) { if(a[i + tmp1] != b[tmp2 - i]) { flag = false; break; } } if(flag) { cout<<"yes"<<endl; cout<<tmp1<<" "<<tmp2<<endl; } else { cout<<"no"<<endl; } return 0; }
Problem 451C. Predict Outcome Of The Game
题目大意:
现在有三个队伍参加n场比赛, 每场比赛只会有1个队伍获胜,对于单场的比赛不会出现平局,现在告诉你三个队伍中第一个队伍与第二个队伍的分差的绝对值 d1, 第二个队伍与第三个队伍的分差 d2 , 一共已经进行了k场比赛, 一共有n场比赛,问是否会出现在n场比赛都结束之后三个队伍的得分一样, 若可能就输出“yes"否则输出"no"
大致思路:
首先设三支队伍当前k场比赛的得分分别是 x1,x2,x3那么 | x1 - x2 | = d1 , | x2 - x3 | = d2,x1 + x2 + x3 = k,
要想最终的结果为平局那么应该有 0 <= xi <= n / 3 ( n % 3 == 0) 是前提
所以可以分情况讨论:
当 x1 <= x2 && x2 <= x3 或 x1 >= x2 && x2 <= x3 或 x1 <= x2 && x2 >= x3 或 x1 >= x2, x2 >= x3, 这样子分四种情况讨论求出x1,x2,x3之后判断解是否合法即可
代码如下:
Result : Accepted Memory : 4 KB Time : 623 ms
/* * Author: Gatevin * Created Time: 2014/7/30 21:07:53 * File Name: test.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; int t; lint n,k,d1,d2,x1,x2,x3; bool check(lint x) { if(0 <= x && x <= n / 3) { return true; } return false; } int main() { cin>>t; while(t--) { cin>>n>>k>>d1>>d2; if(n % 3) { cout<<"no"<<endl; continue; } if((k + d2 - d1) % 3 == 0 && (k + d2 + 2*d1) % 3 == 0 && (k - 2*d2 - d1) % 3 == 0) { x1 = (k + d2 + 2*d1) / 3; x2 = (k + d2 - d1) / 3; x3 = (k - 2*d2 - d1) / 3; if(x1 >= x2 && x2 >= x3 && check(x1) && check(x2) && check(x3)) { cout<<"yes"<<endl; continue; } } if((k - d1 - d2) % 3 == 0 && ( k + 2*d1 - d2) % 3 == 0 && (k - d1 + 2*d2) % 3 == 0) { x1 = (k + 2*d1 - d2) / 3; x2 = (k - d1 - d2) / 3; x3 = (k - d1 + 2*d2) / 3; if(x1 >= x2 && x2 <= x3 && check(x1) && check(x2) && check(x3)) { cout<<"yes"<<endl; continue; } } if((k + d2 + d1) % 3 == 0 && (k + d2 - 2*d1) % 3 == 0 && (k - 2*d2 + d1) % 3 == 0) { x1 = (k + d2 - 2*d1) / 3; x2 = (k + d2 + d1) / 3; x3 = (k - 2*d2 + d1) / 3; if(x1 <= x2 && x2 >= x3 && check(x1) && check(x2) && check(x3)) { cout<<"yes"<<endl; continue; } } if((k + d1 - d2) % 3 == 0 && (k - 2*d1 - d2) % 3 == 0 && (k + d1 + 2*d2) % 3 == 0) { x1 = (k - 2*d1 - d2) / 3; x2 = (k + d1 - d2) / 3; x3 = (k + d1 + 2*d2) / 3; if(x1 <= x2 && x2 <= x3 && check(x1) && check(x2) && check(x3)) { cout<<"yes"<<endl; continue; } } cout<<"no"<<endl; } return 0; }
题目大意:
现在定义Good String:一个string将其中连续的相同的字符合并之后得到的字符串如果是回文串,就成这个String 是Good String, 现在给出一个只包含字母'a' 和 ‘b' 的字符串, 问其字串中长度为奇数的 Good String 有多少个, 长度为偶数的Good String 有多少个
大致思路:
首先要明白, 既然给出的字符串中只有'a' 和 ‘b'这两种字符, 那么要想最终得到的是Good String ,其首部和结尾都必须是相同的字符,可以证明如果在原字符串中选择两个相同的字符作为首尾,得到的子串一定是Good String
证明如下:
首先选取的字符串一定是首尾相同的字符 x, 那么由于在合并连续的字符后得到的字符串一定是a和b交互出现,不可能在合并之后还有连续相同的字符,那么就说明所有的字符x在合并之后得到的新串中一定是都占据奇数位或者都占据偶数位, 而相应的b所占据的一定是对立的全偶数位或全奇数位
由于选择的是首尾都相同的字符,可以发现在合并相同字符之后得到的一定是奇数长度的新串,如果是偶数长度,那么首位 a(1) 和结尾 a(2*k)将分别占据不同奇偶性的位,与同样的字符占据的只是一种奇偶位矛盾,故不成立。而既然合并之后得到的字符串是奇数位的长度且a,b交互的,由对称性一定是回文串
所以如果选取的是首尾相同字符的字串,该字串一定是Good String,证毕
而对于首位不相同的字串, 合并同类项之后得到的首位一定不相同, 不可能是Good String
那么Good String 类型的字串的个数,其实就是从原字符串中选取两个位置, 这两个位置的字符相同,有多少种选法
要求奇数长度或者偶数长度只需要注意一下两个位置的差值+1 是奇数还是偶数就可以了
那么我们可以统计一下在奇数位置的a,b和偶数位置的a,b的数量,便可以计算出来了,不需要暴力一个个地查,公式见代码
代码如下:
Result : Accepted Memory : 256 KB Time : 31 ms
/* * Author: Gatevin * Created Time: 2014/7/30 22:02:29 * File Name: test.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; string s; lint odda,oddb; lint evena, evenb; int main() { cin>>s; odda = oddb = evena = evenb = 0; for(int i = 0; i < s.length(); i++) { if(s[i] == 'a' && ((i + 1)&1)) { odda++; } if(s[i] == 'a' && (i & 1)) { evena++; } if(s[i] == 'b' && (i & 1)) { evenb++; } if(s[i] == 'b' && ((i + 1) & 1)) { oddb++; } } lint even = 0; lint odd = 0; even = odda*evena + oddb*evenb; odd = odda*(odda + 1) / 2 + oddb*(oddb + 1) / 2 + evena*(evena + 1)/2 + evenb*(evenb + 1) / 2; cout<<even<<" "<<odd<<endl; return 0; }
Problem 451E. Devu And Flowers
题目大意:
现在有n个盒子, 第 i 个盒子里装有 f [ i ] 支花, 每个盒子里装的花完全相同,n个盒子里的花的颜色都不相同,现在需要从这n个盒子当中拿出s支花, 输出有多少种方法, 输出结果需要模上1000000007
大致思路:
设 all 表示所有的花的数量
定义集合:
A[ i ] = { 从第 i 个盒子中取的花超出了第 i 个盒子允许的上线} , 全集为U,那么
answer = Card( U ) - Card( A[1] | A[2] | A[3] | .... | A[ n ] )
那么由容斥原理有
首先Card( U ) = C( all , s)
对于Card( A[ i ] )可以这样想:
第 i 个盒子当中拿出来的花首先拿出 f [ i ] + 1支保证条件成立, 然后剩下需要拿的 s - (f [ i ] + 1) 支花就需要分成n个组,可以有组为空,这样根据挡板法相当于 s - (f [ i ] + 1) + n 个相同物品分成n组,每组至少1个,这样有C(s - (f [ i ] + 1) + n - 1, n - 1) 种取法
对于Card(A[ i ] & A[ j ])之类的同理
接下来就是排列组合数的问题,由于s可以达到10^12显然开数组记录C[ n ][ m ]的所有值是不明智的,这里可以用到 Lucas 定理也就是:
C(n, m) % p = C(n / p, n / p) * C(n % p, m % p) % p
(lucas(n,m,p) = cm(n%p, m%p)*lucas(n/p, m/p, p),, lucas(x, 0, p) = 1)
代码如下:
Result : Accepted Memory : 4 KB Time : 951 ms
/* * Author: Gatevin * Created Time: 2014/7/31 19:24:57 * File Name: test.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; lint s; lint f[22]; int n; const lint mod = 1000000007LL; lint quick_pow(lint base, lint pow) { lint ret = 1; while(pow) { if(pow & 1) { ret = (ret*base) % mod; } base = (base*base) % mod; pow >>= 1; } return ret; } lint getc(lint a, lint b) { if(a < b) return 0; if( b > a - b) b = a - b; lint s1 = 1, s2 = 1; for(lint i = 0; i < b; i++) { s1 = (s1*(a - i)) % mod; s2 = (s2*(i + 1)) % mod; } return s1*quick_pow(s2, mod - 2) % mod; } lint lucas(lint a, lint b) { if(b == 0) return 1; return getc(a % mod, b % mod) * lucas(a / mod, b / mod) % mod; } lint solve() { lint ans = 0; for(int i = 0; i < (1 << n); i++) { lint sign = 1; lint sum = s; for(int j = 0; j < n; j++) { if(i & ( 1 << j)) { sum -= f[j] + 1; sign *= -1; } } if(sum < 0) { continue; } ans = (ans + (sign*lucas(sum + n - 1, n - 1) + mod) % mod) % mod; ans %= mod; } return (ans + mod) % mod; } int main() { cin>>n>>s; for(int i = 0; i < n; i++) { cin>>f[i]; } lint answer = solve(); cout<<answer<<endl; return 0; }