BestCoder Round #81

A题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5670

官方题解:

红、绿、蓝分别表示0、1、2,每次操作就相当于+1,原问题就转化为求nn的三进制

表示的最低的mm位,即求 nn mod 3^m3m的三进制表示。

复杂度 O(m)O(m)

我的理解:简单递推 不过读懂题意对我来说有点难


#include<cstdio>
#include<cstring>
using namespace std;
#define ll __int64
char ch[5]="RGB";
char s[31];
int main()
{
    int T,m,i;
    ll n;
    scanf("%d",&T);
    while(T--)
    {
        memset(s,0,sizeof(s));
        scanf("%d%I64d",&m,&n);
        for(i=m-1;i>=0;i--)
        {
            s[i]=ch[n%3];
            n/=3;
        }
        printf("%s\n",s);
    }
}

B题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5671

官方题解:

对于交换行、交换列的操作,分别记录当前状态下每一行、每一列是原始数组的哪一行、哪一列即可。

对每一行、每一列加一个数的操作,也可以两个数组分别记录。注意当交换行、列的同时,也要交换增量数组。

输出时通过索引找到原矩阵中的值,再加上行、列的增量。

复杂度O(q+mn)O(q+mn)

我的理解:简单题,用row col数组标记下当前矩阵的ij位置是原先的哪一行那一列即可,每次都在初始矩阵上进行加减操作

#include<cstdio>
#include<cstring>
using namespace std;
const int N=1005;
int num[1005][1005],row[N],line[N];
int i,j,n,m,q,tmp,T,a,b,c;
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&q);
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=m;j++)
                scanf("%d",&num[i][j]);
        }
        for(i=1;i<=n;i++) row[i]=i;
        for(i=1;i<=m;i++) line[i]=i;
        while(q--)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(a==1)
            {
                int tmp=row[b];
                row[b]=row[c];
                row[c]=tmp;
            }
            else if(a==2)
            {
                int tmp=line[b];
                line[b]=line[c];
                line[c]=tmp;
            }
            else if(a==3)
            {
                for(i=1;i<=m;i++)
                    num[row[b]][i]+=c;
            }
            else
            {
                for(i=1;i<=n;i++)
                    num[i][line[b]]+=c;
            }
        }
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=m;j++)
            {
                if(j==1)printf("%d",num[row[i]][line[j]]);
                else printf(" %d",num[row[i]][line[j]]);
            }
            printf("\n");
        }
    }
    return 0;
}

C题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5672

官方题解:

有一个明显的性质:如果子串(i,j)(i,j)包含了至少kk个不同的字符,那么子串(i,k),(j < k < length)(i,k),(j<k<length)也包含了至少kk个不同字符。

因此对于每一个左边界,只要找到最小的满足条件的右边界,就能在O(1)O(1)时间内统计完所有以这个左边界开始的符合条件的子串。

寻找这个右边界,是经典的追赶法(尺取法,双指针法)问题。维护两个指针(数组下标),轮流更新左右边界,同时累加答案即可。复杂度 O(length(S))O(length(S))

我的理解:简单爆搜,从左往右搜,用cnt记录当前不同的字符个数,若cnt<k,while循环知道cnt=k为止,如果无法满足此条件说明不在存在该种字符串了,跳出循环输出答案

如果cnt==k,答案增加l-j,其中l为总长度,j为当前字符串尾的长度,i和j记录的是当前字符串的头和尾

#include<cstdio>
#include<cstring>
#define ll __int64
using namespace std;
char s[1000005];
ll ans;
int vis[26];
int main()
{
    int T,l,k;
    scanf("%d",&T);
    while(T--)
    {
        memset(vis,0,sizeof(vis));
        scanf("%s",s);
        l=strlen(s);
        scanf("%d",&k);
        ans=0;
        int i=0,j=0,cnt=1;
        vis[s[0]-'a']++;
        while(i<l)
        {
            while(cnt<k)
            {
                j++;
                if(j==l)break;
                if(vis[s[j]-'a']==0)cnt++;
                vis[s[j]-'a']++;
            }
            if(cnt<k)break;
            ans+=(l-j);
            vis[s[i]-'a']--;
            if(vis[s[i]-'a']==0)cnt--;
            i++;
        }
        printf("%I64d\n",ans);
    }
}

D题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5673

官方题解:

记路径长度为nn,那么机器人最多向右走\lfloor \frac{n}{2} \rfloor2n步并向左走\lfloor \frac{n}{2} \rfloor2n步。

Ans(n) = \sum_{i=0}^{\lfloor \frac{n}{2} \rfloor} C_n^{2i} \ Catalan(i)Ans(n)=i=02nCn2i Catalan(i) 其中Catalan(n)Catalan(n)表示第nn个卡特兰数。

卡特兰数定义:Catalan(n)=\frac{C_{2n}^n}{n+1}Catalan(n)=n+1C2nn

递推公式Catalan(n)=\frac{4n-2}{n+1}\ Catalan(n-1)Catalan(n)=n+14n2 Catalan(n1)

基于nn的取值范围,此题可以预处理出1,000,0011,000,001以内的乘法逆元、卡特兰数。

每次询问,都可以递推组合数,或者提前一次性预处理好阶乘和阶乘的逆元得到组合数;累加组合数与相应卡特兰数的乘积,得到答案。

事实上,Ans(n)Ans(n)是第nn个默慈金数,还有更高效的递推公式:

M_{n+1}=M_n+ \sum_{i=0}^{n-1}M_i M{n-1-i} = \frac{(2n+3)M_n+3nM_{n-1}}{n+3}Mn+1=Mn+i=0n1MiMn1i=n+3(2n+3)Mn+3nMn1

我的思考:get新知识 1.卡特兰数 2.线性求逆元 3.默慈金数 此题为默慈金数的应用,也是卡特兰数的经典应用之一,基本上就是裸题,两者效率相差较大

#include<cstdio>//默慈金数
#define ll __int64
using namespace std;
const int mod=1e9+7;
const int N=1000009;
ll dp[N],m[N];
int main()
{
    int T,n;
    m[1]=1;
    for(int i=2;i<=N-9;i++)
        m[i]=((mod-mod/i)*m[mod%i])%mod;
    dp[1]=1;dp[2]=2;dp[3]=4;
    for(ll int i=4;i<=N-9;i++)
    {
        dp[i]=((dp[i-1]*(2*(i-1)+3)+3*(i-1)*dp[i-2])%mod*m[i-1+3])%mod;
    }
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        printf("%d\n",dp[n]);
    }
    return 0;
}

#include<cstdio>//卡特兰数
#include<cstring>
using namespace std;
#define ll __int64
const int N = 1000100;
const int mod = 1000000000 + 7;
ll inv[N],h[N],c[N];
int T,n,m;
void init()
{
    inv[1]=1;
    for(int i=2;i<N;i++)
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
int main()
{
    init();
    scanf("%d",&T);
    h[1]=h[0]=1;
    for(int i=2;i<N;i++)
        h[i]=h[i-1]*(4*i-2)%mod*inv[i+1]%mod;
    while(T--)
    {
        scanf("%d",&n);
        ll ans=1;
        c[0]=1;
        for(int i=1;i<=n;++i)
            c[i]=c[i-1]*(n-i+1)%mod*inv[i]%mod;
        for(int i=1;i<=n/2;++i)
        {
            int k=n-(i<<1);
            ans = (ans+h[i]*c[k])%mod;
        }
        printf("%I64d\n", ans);

    }
    return 0;
}


你可能感兴趣的:(BestCoder Round #81)