AtCoder ARC081 部分题解(DEF)
题目链接
D Coloring Dominoes (递推)
题意:
给一个 2 ∗ n 2*n 2∗n 矩阵,用 1 ∗ 2 1*2 1∗2 或者 2 ∗ 1 2*1 2∗1 的多米诺骨牌填充,现在要给骨牌涂色,共有三种颜色,相邻的骨牌不能涂相同的颜色,问一个有多少种方案 ( m o d 1 e 9 + 7 ) (mod\ 1e9+7) (mod 1e9+7)
思路:
我们从左到右要么是两个
1
∗
2
1*2
1∗2 要么是一个
2
∗
1
2*1
2∗1 ,我们设两个
1
∗
2
1*2
1∗2 为
X
X
X ,一个
2
∗
1
2*1
2∗1 为
Y
Y
Y 。
考虑递推,假设当前方案数为
s
u
m
sum
sum 。
X
→
Y
:
s
u
m
=
s
u
m
∗
1
X \to Y:sum=sum*1
X→Y:sum=sum∗1
X
→
X
:
s
u
m
=
s
u
m
∗
3
X \to X:sum=sum*3
X→X:sum=sum∗3
Y
→
X
:
s
u
m
=
s
u
m
∗
2
Y \to X:sum=sum*2
Y→X:sum=sum∗2
Y
→
Y
:
s
u
m
=
s
u
m
∗
2
Y \to Y:sum=sum*2
Y→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 S ,要求求出最短的字符串 T T T 不是 S S S 的子串,子串不一定连续。长度相同去字典序最小
思路:
首先从后往前,以每一次 26 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] [L1,R1],[L2,R2]...[Ln,Rn] ,我们可以很容易得到答案的长度应该为 n + 1 n+1 n+1 ,因为小于等于 n n n 的字符串都为 S S S 的子串。那么我们利用贪心的思维,在 [ 1 , L 1 ] [1,L_1] [1,L1] 中寻找一个26个字母中没出现的且最小的字母做为 T [ 1 ] T[1] T[1] 必然是正确的,因为若 T [ 1 ] T[1] T[1] 在 [ 1 , L 1 ] [1,L_1] [1,L1] 中出现,那么以 T [ 1 ] T[1] T[1] 开头的长度为 n + 1 n+1 n+1 的串一定是 S S S 的子串,那么确定了 T [ 1 ] T[1] T[1] 接下来就往后找到第一个出现 T [ 1 ] T[1] T[1] 的位置为 p o s 1 pos_1 pos1 下面就是在 [ p o s 1 , R 1 ] [pos_1,R_1] [pos1,R1] 中找到一个26个字母中没出现的且最小的字母做为 T [ 2 ] T[2] T[2] ,依次下去,得到 T 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 n∗m 的矩阵,每一个格子有黑白两种颜色,每次操作可以翻转一行或者一列的颜色,问最大可以获得的全黑子矩阵的面积。
思路:
假设黑为 1 1 1,白为 0 0 0 ,首先由于是翻转操作,那么对于一个 2 ∗ 2 2*2 2∗2 的矩阵无论怎么翻转它的 4 4 4 个数字的异或值不变。故题解有一个结论:一个 n ∗ m n*m n∗m 的矩阵要变成全黑,必须满足其中的 2 ∗ 2 2*2 2∗2 子矩阵必须是异或和为 0 0 0 ,通过这个结论,我们可以用一个矩阵处理出所有的 2 ∗ 2 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