CF Round#626
CF Round#626
A.水~##
B.数学##
题意:
分别给定你两个长度为n,m的字符串A,B,都是由0,1构成
矩阵每个元素aij=Ai*Bj
现在给定k,问这个矩阵当中有多少个子矩阵,使得子矩阵的所有元素为1,且1的数量刚好为k
思路:
这道题目看起来很难,但是稍加分析,不难发现,所求的k是个面积,那么面积就应该由长乘宽组成,换句换说,我们现在就是要将k进行拆解两个整数,使得k=m * (k/m),那么为了子矩阵内全是1,那么我们无非就要使长或宽均是1。
那么问题就转变成了在A上寻找一段长度为m连续为1的子串,B上寻找长度为k/m连续为1的子串,根据乘法原理,相乘即可。
那么问题的关键点就成为了长度为k,连续为1的子串有多少
On 的做法就是从左往右扫一遍,初始化cur=0,遇到1,cur++,savea[cur]++;遇到0,cur=0;
处理后,再从右往左扫一遍,savea[i-1]+=savea[i],这样做的目的模拟一下下就懂了,savea[x]就是长度为x的数量了
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
#define INF 1e10+5
#define MAXN 50010
#define MINN -105
typedef long long int LL;
int main()
{
LL n,m,k;
LL a[MAXN],b[MAXN],savea[MAXN],saveb[MAXN];
memset(savea,0,sizeof(savea));
memset(saveb,0,sizeof(saveb));
cin>>n>>m>>k;
LL cur=0;
for(LL i=1;i<=n;i++)
{
cin>>a[i];
if(a[i])cur++;
else cur=0;
savea[cur]++;
}
for(LL i=n;i>=2;i--)
savea[i-1]+=savea[i];
cur=0;
for(LL i=1;i<=m;i++)
{
cin>>b[i];
if(b[i])cur++;
else cur=0;
saveb[cur]++;
}
for(LL i=m;i>=2;i--)
saveb[i-1]+=saveb[i];
LL ans=0;
for(LL i=1;i<=k;i++)
{
if(i>n)break;
if((k/i)>m)continue;
if(k/i*i==k)
ans+=savea[i]*saveb[k/i];
}
cout<<ans<<endl;
return 0;
}
C.模拟##
这题看懂题目意思很重要,他是给一个字符串由 ( 和 ) 组成,问你最小操作长度多长,使得括号匹配正确。每次操作可以任意调换长度内的顺序.
思路:首先,他只能调换顺序,也就是是"("和")"的数量要一样,其次,你从左往右扫,to记录当前(的数量,如果为正,说明(比)多多少,也就是是只要为正就可以完全抵消,如果为负,那么要记录te,进行交换的区间是多长,一旦to==0,那么就要更新ans,清空te
#include<iostream>
#include<cstring>
#include<string>
using namespace std;
#define INF 1e10+5
#define MAXN 105
#define MINN -105
typedef long long int LL;
int main()
{
int n ;
string s ;
cin>>n>>s;
int c=0,to=0,te=0,ans=0;
for(int i=0;i<n;++i)(s[i]==')')?--c:++c ;
if(c){cout<<-1<<endl;return 0;}
for(int i=0;i<n;++i)
{
if(s[i]=='(')
{
if(to<0)te++;
to++;
}
else to--;
if(to==0)ans+=(te*2),te=0;
}
cout<<ans<<endl;
}