AtCoder ARC081 部分题解(DEF)
题目链接
D Coloring Dominoes (递推)
题意:
给一个 矩阵,用 或者 的多米诺骨牌填充,现在要给骨牌涂色,共有三种颜色,相邻的骨牌不能涂相同的颜色,问一个有多少种方案
思路:
我们从左到右要么是两个
要么是一个
,我们设两个
为
,一个
为
。
考虑递推,假设当前方案数为
。
在注意一下初始化就行。
代码:
#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 (贪心 + 思维)
题意:
给定一个字符串 ,要求求出最短的字符串 不是 的子串,子串不一定连续。长度相同去字典序最小
思路:
首先从后往前,以每一次 个字母恰好都出现来划分区间 ,我们可以很容易得到答案的长度应该为 ,因为小于等于 的字符串都为 的子串。那么我们利用贪心的思维,在 中寻找一个26个字母中没出现的且最小的字母做为 必然是正确的,因为若 在 中出现,那么以 开头的长度为 的串一定是 的子串,那么确定了 接下来就往后找到第一个出现 的位置为 下面就是在 中找到一个26个字母中没出现的且最小的字母做为 ,依次下去,得到 ,就是答案了。
代码:
#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 (思维 + 单调栈)
题意:
给定一个 的矩阵,每一个格子有黑白两种颜色,每次操作可以翻转一行或者一列的颜色,问最大可以获得的全黑子矩阵的面积。
思路:
假设黑为 ,白为 ,首先由于是翻转操作,那么对于一个 的矩阵无论怎么翻转它的 个数字的异或值不变。故题解有一个结论:一个 的矩阵要变成全黑,必须满足其中的 子矩阵必须是异或和为 ,通过这个结论,我们可以用一个矩阵处理出所有的 的异或和,那么就相当于在矩阵中找到最大子矩阵,使得矩阵内全为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