超级英雄的战斗(fight)
【 问题描述】
美国队长和钢铁侠正在与很多外星人战斗。 这场战斗的持续时间是 t
分钟, 每分钟美国队长和钢铁侠都可能消灭 0 个或 1 个外星人。 我们用一
个长度为 t 的字符串 S 来描述这场战斗, 每个字符代表一分钟内发生的情
况: M 表示只有美国队长消灭了 1 个外星人, G 表示只有钢铁侠消灭了 1
个外星人, T 表示两人同时消灭了 1 个外星人, 不存在某 1 分钟两人都没
有消灭外星人。
钢铁侠知道一般情况下他们两个的战斗能力比是 m:g, 表示在相同时
间内如果美国队长消灭了 mk 个敌人, 钢铁侠应当能消灭 gk 个。 战斗之
后, 钢铁侠发现, 并不是所有连续时间段都符合一般情况的。 用 S 的一个
子串 S2=S[l…r]表示一个连续时间段, 我们称 S2 是“好” 的, 当且仅当在
第 l 分钟到第 r 分钟( 两端均包含) 美国队长消灭的敌人数和钢铁侠消灭
的敌人数之比恰好等于 m:g。
钢铁侠需要计算最长的“好” 的 S 的非空子串, 最长的“ 好” 的子串
的数量, 以及 S 的所有“好” 的非空子串的数量。 如果字符串一样但位置
不同分开计算数量。
【 输入格式(fight.in)】
输入第一行包含 3 个非负整数: t,m 和 g。
第二行包含一个长度为 t 的字符串 S, 含义如前所说。
【 输出格式(fight.out)】
输出两行。 第一行包含 3 个整数 l,r 和 cnt, 表示 S 的子串 S[l…r]是最
长的“好” 子串( S 的编号从 1 开始) , 如果有多个输出最左边的子串,
以及与它等长的“ 好”的子串的数量。 第二行包含 1 个整数, 表示 S 的“好”
的非空子串数量。 如果不存在“好” 的非空子串, 只输出”A weird fight”( 不
包含引号) 。
【 样例输入 1】
6 1 2
GTGGTT
【 样例输出 1】
1 6 1
6
【 样例输入 2】
2 3 2
TMG
【 样例输出 2】
A weird fight
显然,暴力枚举,就是预处理一个前缀和枚举左区间有区间统计即可。
钢铁侠的前缀和为G[i],美国队长的前缀和为M[i]。
如果要满足条件,对于左右区间,一定满足下列等式:
g
/
m
=
(
G
[
r
]
−
G
[
l
−
1
]
)
/
(
M
[
r
]
−
M
[
l
−
1
]
)
g/m=(G[r]-G[l-1])/(M[r]-M[l-1])
g/m=(G[r]−G[l−1])/(M[r]−M[l−1])
变形,就是:
g
∗
(
M
[
r
]
−
M
[
l
−
1
]
)
=
m
∗
(
G
[
r
]
−
G
[
l
−
1
]
)
g*(M[r]-M[l-1])=m*(G[r]-G[l-1])
g∗(M[r]−M[l−1])=m∗(G[r]−G[l−1])
展开,得:
g
∗
M
[
r
]
−
g
∗
M
[
l
−
1
]
=
m
∗
G
[
r
]
−
m
∗
G
[
l
−
1
]
g*M[r]-g*M[l-1]=m*G[r]-m*G[l-1]
g∗M[r]−g∗M[l−1]=m∗G[r]−m∗G[l−1]
移项,得:
g
∗
M
[
r
]
−
m
∗
G
[
r
]
=
g
∗
M
[
l
−
1
]
−
m
∗
G
[
l
−
1
]
g*M[r]-m*G[r]=g*M[l-1]-m*G[l-1]
g∗M[r]−m∗G[r]=g∗M[l−1]−m∗G[l−1]
显然,我们可以先求出每一个l值,在枚举r的时候直接查找用数组标记的l值即可。
程序注意:枚举i时,对于r是i,对于l是l+1,所以作为r的i不能直接调用当前i的l。
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define maxn 120000
#define LL long long
int t,m,g;
char a[maxn];
int G[maxn],M[maxn];
int ans=0,maxcnt=0,ans_left=0,maxlen=0;
map<int,int> first,cnt;
int main()
{
freopen("fight.in","r",stdin);
freopen("fight.out","w",stdout);
cin>>t>>m>>g;
for (int i=1;i<=t;++i)
{
cin>>a[i];
G[i]=G[i-1],M[i]=M[i-1];
if (a[i]=='T') G[i]++,M[i]++;
else if (a[i]=='M') M[i]++;
else if (a[i]=='G') G[i]++;
}
first[0]=cnt[0]=1;
for (int i=1;i<=t;++i)
{
LL val=(LL)m*G[i]-g*M[i];//计算Right的值
if (!first[val]) first[val]=i+1,cnt[val]=1;
//同时i也作为i的值进行运算 实际的含义是left=1
else
{
ans+=cnt[val];
if (i-first[val]>maxlen)
{
maxlen=i-first[val];
ans_left=first[val];
maxcnt=1;
}
else if (i-first[val]==maxlen) maxcnt++;
cnt[val]++;
//注意顺序不能乱:Right(i)使用过才能使用Left(i+1),该left状态在Right下不合法
}
}
if (!maxlen) cout<<"A weird fight\n";
else cout<<ans_left<<' '<<ans_left+maxlen<<' '<<maxcnt<<'\n'<<ans<<'\n';
return 0;
}
方方方
我们知道,对于三个点的曼哈顿距离只和为:左上角和右下角组成的矩形。
对于每一个矩形,若长度为x,y,则移动有
(
n
−
x
+
1
)
(
m
−
y
+
1
)
(n-x+1)(m-y+1)
(n−x+1)(m−y+1)个
证明:行列。显而易见。
若长度为x,y,则一共选择的方案数就是
6
∗
(
n
−
2
)
(
m
−
2
)
6*(n-2)(m-2)
6∗(n−2)(m−2) 种
证明:
1.若一个是左上角,一个是右下角,合法的第三个点就是中间矩形部分;左下角和右上角同样。故总方案数为:
2
∗
(
x
−
2
)
∗
(
y
−
2
)
2*(x-2)*(y-2)
2∗(x−2)∗(y−2)
2.若只有一个是角落的,假设是左上角,且另外亮点一定不再角落上,同样是在右边的和下面的边中去掉两个端点的位置;右上角,右下角,左下角同理。故总方案数为:
4
∗
(
x
−
2
)
∗
(
y
−
2
)
4*(x-2)*(y-2)
4∗(x−2)∗(y−2)
因此xy的矩形拥有:
(
n
−
x
+
1
)
∗
(
n
−
y
+
1
)
∗
6
∗
(
x
−
2
)
∗
(
y
−
2
)
(n-x+1)*(n-y+1)*6*(x-2)*(y-2)
(n−x+1)∗(n−y+1)∗6∗(x−2)∗(y−2)
直接枚举即可。
#include<bits/stdc++.h>
using namespace std;
#define maxn
#define P 1000000007
inline void read(long long &readnum)
{
long long s=0,w=1;char c=getchar();
while (c<'0' || c>'9') {if (c=='-') w=-1;c=getchar();}
while (c>='0' && c<='9') s=s*10+c-48,c=getchar();
readnum=s*w;
}
long long n,m,l,r;
long long ans=0;
inline long long num(long long x,long long y) {
return (n-x+1)*(m-y+1)%P;
}
inline long long sum(long long x,long long y) {
return 6*(x-2)*(y-2)%P;
}
int main(void)
{
freopen("orzfang.in","r",stdin);
freopen("orzfang.out","w",stdout);
read(n);
read(m);
read(l);
read(r);
for (long long i=3;i<=n;++i)
for (long long j=3;j<=m;++j)
if ((i+j-2)*2>=l && (i+j-2)*2<=r)
ans=(ans+(num(i,j)*sum(i,j))%P)%P;
cout<<ans<<endl;
return 0;
}