Japanese Student Championship 2019 Qualification

转战Atcoder,这场比赛的名字看起来就很有趣的样子,很盛大的感觉,那当然赶紧来玩玩

A - Takahashi Calendar

思路:定义新日历算法,求特定日子数
典型的签到题,当然是直接在新日历上跑一遍即可
 

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int mm,dd;
    scanf("%d%d",&mm,&dd);
    int sum = 0;
    for(int m=1;m<=mm;m++)
    {
        for(int d=1;d<=dd;d++)
        {
            int d1 = d%10;
            int d10 = d/10;
            if(d1>=2&&d10>=2&&d1*d10==m)
                sum++;
        }
    }
    printf("%d\n",sum);
    return 0;
}

B - Kleene Inversion

思路:看似一个很复杂的逆序对问题,立刻想着像逆序对上套,实则不然,由于子串长度过短,完全可以直接暴力预处理掉子串,处理掉该串中的大小和该位置后的大小,最后只是个小小数学计算问题了。而对于大数带模除法,直接一个逆元搞定
 

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int maxn = 2e3+5;

const ll mod = (ll)(1e9+7);

int aa[maxn],rk[maxn],ne[maxn];

ll gcd(ll a,ll b, ll &x,ll &y)
{
    if(b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    else
    {
        ll r = gcd(b,a%b,y,x);
        y -= x * (a / b);
        return r;
    }
}

ll ni(ll a,ll n)
{
    ll x, y;
    ll d = gcd(a, n, x, y);
    ll ans;
    if(d == 1)
    {
        x %= n;
        x += n;
        x %= n;
        ans = (x % n);
        return ans;
    }
    else
    {
        return -1;
    }
}
int main()
{
    int n,k;
    ll b=0LL;
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&aa[i]);
        rk[i] = 0;
        ne[i] = 0;
    }
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<i;j++)
        {
            if(aa[i]>aa[j])
                rk[i]++;
        }
        for(int j=i;j<n;j++)
        {
            if(aa[i]>aa[j])
            {
                ne[i]++;
                rk[i]++;
            }
        }
        b = (b + (((((((ll)rk[i]*(ll)(k-1))%mod)*(ll)k)%mod)*ni(2,mod))%mod) + (((ll)ne[i] * (ll)k)%mod)) % mod;
    }
    printf("%lld\n",b);
    return 0;
}

C - Cell Inversion

思路:翻转序列题,也是个看似很困难的题,但细心品味也确实是一道很有意思的题目

首先,题目中明确说明了每个点都必须翻转一次,这就意味着必会存在n组翻转组合,问题的关键就是找出个n种组合方式,至于组合之间的顺序并不重要

于是二话不说,先来个关于n的全排列。

接下来,由于序列翻转的核心就在元素变换交叉处。

不难发现,这题可以转化为将变换端点视为括号的括号匹配题,由于每个点比对应一个括号,则相邻两个括号之间的关系将成为关键。
若相邻两个点均为左括号或均为右括号,由于外侧点多进行了一次变换,导致这两点必然不同;相应若相邻两点括号相同,则刚好形成一种无缝连接,使得变换结果必相同。

当然,一旦确定了每个点所属的变换类型,相应的对应匹配对也不重要了,因为只要能满足括号匹配规则(允许括号交叉,不允许右括号先于左括号),匹配出来的结果必能相同。

这样算来,只需要计算每次右括号出现时前面已出现的左括号数量,作为方案数,并任意消耗一个左括号进行匹配

最后,就可以根据相邻点间的元素关系预处理各个点的变换类型,然后再遍历计算方案数即可

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int maxn = 2e5+5;
const ll mod = ll(1e9+7);

char s[maxn];
int s2[maxn];

int main()
{
    int n;
    scanf("%d%s",&n,s);
    s2[0] = 1;
    for(int i=1;i<2*n;i++)
    {
        if(s[i]==s[i-1])
            s2[i] = (s2[i-1]+1)%2;
        else
            s2[i] = s2[i-1];
    }
    ll sum = 1LL;
    for(int i=2;i<=n;i++)
    {
        sum = (sum * (ll)i)%mod;
    }
    //cout << sum << endl;
    int num = 0;
    for(int i=0;i<2*n;i++)
    {
        //out << sum << "*" << num << endl;
        if(s2[i]==1)
            num++;
        else
        {
            sum = (sum * ll(num))%mod;
            //cout << sum << "*" << num << endl;
            num--;
        }
    }
    if(s[0]=='W'||num!=0)
        printf("0\n");
    else
        printf("%lld\n",sum);
    return 0;
}
发布了97 篇原创文章 · 获赞 89 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Owen_Q/article/details/100714751