• Educational Codeforces Round 59 (Rated for Div. 2)


    (本来准备划水,结果被垃圾题艹翻了……)

    T2题意:

    定义一个数$x$的数字根$S(x)$为:将其各位数字相加得到一个新数,再将新数的数字和相加直到得到一个个位数,就是该数的数字根。

    例如:$S(38)=S(3+8=11)=S(1+1=2)=2$

    现在需要求数字根为$x$的从小到大第$k$个数。

    $1leq xleq 9,kleq 10^{12}$。

    题解:

    注意到数字和相加不会改变x对9取余的值。

    那么可以得到:数字根为$x$的数就是满足模$9$的值为$x$的数。

    特别地,数字根为$9$的数满足模$9$的值为$0$。

    然后一行就可以了。

    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    
    using namespace std;
    #define MAXN 100005
    #define MAXM 500005
    #define INF 0x7fffffff
    #define ll long long
    
    inline ll read(){
        ll x=0,f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar())
            if(c=='-')
                f=-1;
        for(;isdigit(c);c=getchar())
            x=x*10+c-'0';
        return x*f;
    }
    
    int main(){
        ll N=read();
        for(ll i=1;i<=N;i++){
            ll k=read(),x=read();
            cout<<x+(k-1)*9<<endl;
        }
        return 0;
    }

    T4题意:

    定义一个$n$次矩阵$A$的$x$次压缩矩阵$B$为:对于任意$i,j$满足$A(i,j)=B(lceil i/x ceil,lceil j/x ceil)$。

    显然对于某些$x$是不存在$x$次压缩矩阵的。

    现在给定一个$01$矩阵$A$,求最大的满足$x|n$的能压缩的$x$。

    $nleq 5200$。

    题解:

    发现压缩的过程就相当于把原矩阵$A$分成若干个$x imes x$的小矩阵,若每个小矩阵内数均相同则$x$次压缩是可行的。

    那么暴力算法就是枚举每个$x$再进行$O(n^2)$枚举判断可行性。

    考虑优化,枚举$k$的过程不太好优化,我们需要一个快速的办法判断一个小矩阵中的数是否相同。

    由于数只有$01$,可以维护二维前缀和,若这个小矩阵内数的和不等于$0$或者$x imes x$则肯定不相同。

    时间复杂度为$O(sum frac{n^2}{x^2})=O(n^2 imes sum frac{1}{x^2})=O(n^2)$。

    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    
    using namespace std;
    #define MAXN 5205
    #define MAXM 500005
    #define INF 0x7fffffff
    #define ll long long
    
    inline int read(){
        int x=0,f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar())
            if(c=='-')
                f=-1;
        for(;isdigit(c);c=getchar())
            x=x*10+c-'0';
        return x*f;
    }
    
    int N,mp[MAXN][MAXN],sum[MAXN][MAXN];
    char str[MAXN];
    
    inline int gets(int x,int y,int xx,int yy){
        return sum[xx][yy]-sum[xx][y]-sum[x][yy]+sum[x][y];
    }
    
    bool check(int x){
        for(int i=1;i<=N;i+=x){
            for(int j=1;j<=N;j+=x){
                if(gets(i-1,j-1,i+x-1,j+x-1)!=0 && gets(i-1,j-1,i+x-1,j+x-1)!=x*x)
                    return 0;
            }
        }
        return 1;
    }
    
    int chg(char ch){
        if(isdigit(ch)) return ch-'0';
        else return ch-'A'+10;
    } 
    
    int main(){
        N=read();
        for(int i=1;i<=N;i++){
            scanf("%s",str);
            for(int j=1;j<=N;j+=4){
                int tp=chg(str[j/4]);
                for(int k=0;k<4;k++)
                    mp[i][j+k]=(bool)(tp&(1<<(4-k-1)));
            }
        }
        for(int i=1;i<=N;i++)
            for(int j=1;j<=N;j++)
                sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+mp[i][j];
        for(int x=N;x>=1;x--){
            if(N%x) continue;
            if(check(x)){
                printf("%d
    ",x);
                return 0;
            }
        }
        return 0;
    }

    T5题意:

    给定一个长度为$n$的$01$串$s$,你可以进行任意次操作:

    每次操作选取一段连续且相等的串$str$,记其长度为$k(1leq k leq n)$。

    将这段串删除并将它右边的串接到左边,同时获得$A_k$的价值。

    求将整个串删除为空串所能获得的最大价值。

    $nleq 100$。

    题解:

    这题当时触及到我的知识盲区了……(菜是原罪)考完才学了一下。

    “区间消消乐”问题可以算作一种单独的$dp$模型,状态一般是

    设$dp(i,j,k)$表示处理子串$[i,j]$,后面带上连续$k$个与$s_j$相同的字符所能获得的最大价值。

    那么每种状态都有如下两种转移过来的方式:

    • 消后面那段颜色相同的。$dp(i,j,k)=dp(i,j-1,0)+A_k+1$
    • 枚举中间的某个断点$mid$,保证$s_{mid}=s_{j}$,此时可以把$[mid+1,j-1]$一段消去,后面接出更长的一段。$dp(i,j,k)=dp(i,mid,k+1)+dp(mid+1,j-1,0)$

    时间复杂度$O(n^4)$。

    这里我个人想再说细一点(因为思维太弱跳不了那么远QAQ):如何想到这个非常规的转移方程?

    注意:以下讨论均是对于子区间[i,j]的处理。

    如果这道题没有“删除后将右边接到左边”这句话,那么转移方程并不复杂:

    $dp(i,j)=max{dp(i,j),dp(i,k-1)+A_{j-k+1}}$,其中$s_k=s_k+1=cdots=s_j$。

    但现在多了这句话,就要考虑怎么在转移时做出“拼接”这种骚操作来。

    看上去有一个显而易见的暴力:枚举删除的区间(两端而不是一端)暴力转移。

    此时状态大概是$dp(i,j,str)$表示区间$[i,j]$后面连着一段$str$。

    由于思维量不大,所以这个方法的复杂度不用算也知道……

    那么观察一下这个方法:记录后面连着一段的思想貌似可行,但真的有必要记录$str$吗?

    现在我们状态中的str还是需要暴力处理,多这一维没有起到实质性的作用。

    回到题目,消除任意一段str时,每一步操作都是消除一段连续字符。

    反过来说,也就是用若干段连续字符必然能拼出任意的str。

    那么我们为什么不能把状态里的str改成数字k,表示后面连着一段长度为k的连续字符呢?

    Nice!我们发现用改完后的状态能够表示出原来的所有状态,这说明这个思路是可做的。

    此时的状态为$dp(i,j,k,0/1)$表示$[i,j]$后面连着$k$个$0/1$。

    我们还可以把$j$右移一位以略去第四维而将定义改成连着$k$个与$s_j$相同的字符。

    无论哪种状态都能够通过本题了。

    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    
    using namespace std;
    #define MAXN 105
    #define MAXM 500005
    #define INF 0x7fffffff
    #define ll long long
    
    inline ll read(){
        ll x=0,f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar())
            if(c=='-')
                f=-1;
        for(;isdigit(c);c=getchar())
            x=x*10+c-'0';
        return x*f;
    }
    
    ll dp[MAXN][MAXN][MAXN];
    ll N,A[MAXN];
    char str[MAXN];
    
    int main(){
        N=read();
        scanf("%s",str+1);
        for(ll i=1;i<=N;i++)
            A[i]=read();
        for(ll i=1;i<=N;i++)
            for(ll k=0;k<=N;k++)
                dp[i][i][k]=A[k+1];
        for(ll l=2;l<=N;l++)
            for(ll i=1;i<=N-l+1;i++){
                ll j=i+l-1;
                for(ll k=0;k<=N;k++){
                    dp[i][j][k]=dp[i][j-1][0]+A[k+1];
                    for(ll l=i;l<j;l++)
                        if(str[l]==str[j]) 
                          dp[i][j][k]=max(dp[i][j][k],dp[i][l][k+1]+dp[l+1][j-1][0]);
                }
            }
        printf("%I64d
    ",dp[1][N][0]);
        return 0;
    }
  • 相关阅读:
    GIT
    curl
    排序算法
    《软件设计师》考点分布
    lua第三方库
    WordPress
    go http
    Unity UI相关总结
    note
    LUA重难点解析
  • 原文地址:https://www.cnblogs.com/YSFAC/p/10328092.html
Copyright © 2020-2023  润新知