Codeforces Round #258 (div2) ABC水题 D规律 E容斥原理

这场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;
}



Problem 451D. Count Good Substrings

题目大意:

现在定义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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值