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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章