AtCoder-ARC081(DEF题解)

AtCoder ARC081 部分题解(DEF)

题目链接

D Coloring Dominoes (递推)

题意:

给一个 2 n 2*n 矩阵,用 1 2 1*2 或者 2 1 2*1 的多米诺骨牌填充,现在要给骨牌涂色,共有三种颜色,相邻的骨牌不能涂相同的颜色,问一个有多少种方案 ( m o d   1 e 9 + 7 ) (mod\ 1e9+7)

思路:

我们从左到右要么是两个 1 2 1*2 要么是一个 2 1 2*1 ,我们设两个 1 2 1*2 X X ,一个 2 1 2*1 Y Y
考虑递推,假设当前方案数为 s u m sum
X Y : s u m = s u m 1 X \to Y:sum=sum*1
X X : s u m = s u m 3 X \to X:sum=sum*3
Y X : s u m = s u m 2 Y \to X:sum=sum*2
Y Y : s u m = s u m 2 Y \to Y:sum=sum*2

在注意一下初始化就行。

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100;
const int mod=1e9+7;

char s1[N],s2[N];
int n;
int f[N];//预处理类型
int cnt;
int main()
{
    scanf("%d",&n);
    scanf("%s",s1+1);
    scanf("%s",s2+1);
    for(int i=1;i<=n;i++){
        if(i+1<=n&&s1[i]==s1[i+1])f[++cnt]=1,i++;
        else{
            f[++cnt]=2;
        }
    }
    ll sum=0;int pre=0;
    if(f[1]==1)sum=6,pre=1;else sum=3,pre=2;//初始化
    for(int i=2;i<=cnt;i++){
        if(pre==1&&f[i]==1){
            sum=(sum*3)%mod;
        }else if(pre==1&&f[i]==2){
            sum=sum%mod;
        }else if(pre==2&&f[i]==1){
            sum=(sum*2)%mod;
        }else if(pre==2&&f[i]==2){
            sum=(sum*2)%mod;
        }
        pre=f[i];
    }
    cout<<sum<<endl;
}

E Don’t Be a Subsequence (贪心 + 思维)

题意:

给定一个字符串 S S ,要求求出最短的字符串 T T 不是 S S 的子串,子串不一定连续。长度相同去字典序最小

思路:

首先从后往前,以每一次 26 26 个字母恰好都出现来划分区间 [ L 1 , R 1 ] , [ L 2 , R 2 ] . . . [ L n , R n ] [L_1,R_1],[L_2,R_2]...[L_n,R_n] ,我们可以很容易得到答案的长度应该为 n + 1 n+1 ,因为小于等于 n n 的字符串都为 S S 的子串。那么我们利用贪心的思维,在 [ 1 , L 1 ] [1,L_1] 中寻找一个26个字母中没出现的且最小的字母做为 T [ 1 ] T[1] 必然是正确的,因为若 T [ 1 ] T[1] [ 1 , L 1 ] [1,L_1] 中出现,那么以 T [ 1 ] T[1] 开头的长度为 n + 1 n+1 的串一定是 S S 的子串,那么确定了 T [ 1 ] T[1] 接下来就往后找到第一个出现 T [ 1 ] T[1] 的位置为 p o s 1 pos_1 下面就是在 [ p o s 1 , R 1 ] [pos_1,R_1] 中找到一个26个字母中没出现的且最小的字母做为 T [ 2 ] T[2] ,依次下去,得到 T T ,就是答案了。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
char s[N];
int l[N],r[N],cnt;
int L[N],R[N];
set<char>jh;
string ans;
bool vis[30];
int main()
{
    scanf("%s",s+1);int jl=0;
    int len=strlen(s+1);
    for(int i=len;i>=1;i--){
        if(jh.size()==0)jl=i;
        jh.insert(s[i]);
        if(jh.size()==26){
            r[++cnt]=jl;//划分区间
            l[cnt]=i;jh.clear();
        }
    }
    int n=cnt;
    for(int i=1,j=cnt;j>=1;j--,i++){
        L[i]=l[j];R[i]=r[j];//区间倒序一下
    }
    if(cnt==0){//特判n=0的情况
        for(int i=1;i<=len;i++){
            vis[s[i]-'a']=1;
        }int p;
        for(int i=0;i<26;i++)if(vis[i]==0){p=i;break;}
        ans+=char(p+'a');cout<<ans<<endl;
        return 0;
    }
    int p=1;//当前位置
    memset(vis,0,sizeof(vis));
    for(int j=1;j<L[1];j++)vis[s[j]-'a']=1;
    int pos=0;//找到的位置
    for(int j=0;j<26;j++)if(!vis[j]){pos=j;break;}
    ans+=char(pos+'a');
    while(p<=len&&s[p]!=pos+'a')p++;
    for(int i=1;i<=n;i++){
        memset(vis,0,sizeof(vis));
        p++;
        for(int j=p;j<=R[i];j++){
            vis[s[j]-'a']=1;
        }
        for(int j=0;j<26;j++)if(!vis[j]){pos=j;break;}
        ans+=char(pos+'a');
        while(p<=len&&s[p]!=pos+'a')p++;
    }
    cout<<ans<<endl;
}

F Filp and Rectangles (思维 + 单调栈)

题意:

给定一个 n m n*m 的矩阵,每一个格子有黑白两种颜色,每次操作可以翻转一行或者一列的颜色,问最大可以获得的全黑子矩阵的面积。

思路:

假设黑为 1 1 ,白为 0 0 ,首先由于是翻转操作,那么对于一个 2 2 2*2 的矩阵无论怎么翻转它的 4 4 个数字的异或值不变。故题解有一个结论:一个 n m n*m 的矩阵要变成全黑,必须满足其中的 2 2 2*2 子矩阵必须是异或和为 0 0 ,通过这个结论,我们可以用一个矩阵处理出所有的 2 2 2*2 的异或和,那么就相当于在矩阵中找到最大子矩阵,使得矩阵内全为0代码中是找全1子矩阵,这就是一个单调栈的问题,相当于一个n次求最大长方形问题.不过还是要注意一些细节问题。

代码:

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
const int N=2e3+10;
char s[N][N];
int tu[N][N];
int n,m;
struct node{
    int w,v;
    node(int a=0,int b=0){w=a;v=b;}
};
stack<node>S;
int h[N],l[N],r[N];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%s",s[i]+1);
    }
    for(int i=1;i<n;i++)
    for(int j=1;j<m;j++){
        int a=s[i][j]=='.'?0:1,b=s[i+1][j]=='.'?0:1,c=s[i+1][j+1]=='.'?0:1,d=s[i][j+1]=='.'?0:1;
        tu[i][j]=!(a^b^c^d);
        if(tu[i][j])tu[i][j]+=tu[i-1][j];//预处理每一列可以延申的高度
    }
    int ans=max(n,m);
    for(int i=1;i<n;i++){
        for(int j=1;j<m;j++){//单调栈
            if(S.empty()){
                l[j]=1;S.push(node(j,tu[i][1]));
            }else{
                int mi=j;
                while(!S.empty()&&S.top().v>=tu[i][j]){
                    int id=S.top().w;r[id]=j;S.pop();
                }
                if(S.empty())mi=1;else mi=S.top().w+1;
                S.push(node(j,tu[i][j]));l[j]=mi;
            }
        }
        while(!S.empty()){int id=S.top().w;r[id]=m;S.pop();}
        for(int j=1;j<m;j++)ans=max(ans,(tu[i][j]+1)*(r[j]-l[j]+1));//注意由于将2*2缩成1,要加1
    }
    cout<<ans<<endl;
}

by YaoYaoLe

猜你喜欢

转载自blog.csdn.net/qq_40400202/article/details/100133533