S t r i n g F o r c e s StringForces StringForces 为什么他们代码能力那么强!!!
A. You Are Given Two Binary Strings…
Solution
显然只需要把最低位的 1 1 1消掉就好了
#include<cstdio>
#include<algorithm>
#include<cstring>
const int MAXN = 1e5+10;
int n,m;
char b1[MAXN],b2[MAXN];
void solve(){
scanf("%s%s",b1+1,b2+1);
n=strlen(b1+1);
m=strlen(b2+1);
std::reverse(b1+1,b1+1+n);
std::reverse(b2+1,b2+1+m);
int pos=1;
while(b2[pos]!='1')
pos++;
int i;
for(i=pos;i;i++){
if(b1[i]=='1')
break;
}
printf("%d\n",i-pos);
}
int main(){
int T;
scanf("%d",&T);
while(T--)
solve();
return 0;
}
B. You Are Given a Decimal String…
Solution
假设从
a
i
a_i
ai转移到
a
j
a_j
aj,可以看作从
0
0
0转移到
(
a
i
−
a
j
+
10
)
%
10
(a_i-a_j+10)\%10
(ai−aj+10)%10
所以可以枚举每次选的两个数
x
,
y
x,y
x,y
设
f
[
n
]
f[n]
f[n]为用这两个数凑出
n
n
n最少要多少步
f
[
x
]
=
f
[
y
]
=
1
f[x]=f[y]=1
f[x]=f[y]=1
转移
f
[
(
n
+
x
)
%
10
]
=
f
[
n
]
+
1
f[(n+x)\%10]=f[n]+1
f[(n+x)%10]=f[n]+1
f
[
(
n
+
y
)
%
10
]
=
f
[
n
]
+
1
f[(n+y)\%10]=f[n]+1
f[(n+y)%10]=f[n]+1
然后像
s
p
f
a
spfa
spfa那样转移就好了
如果不好理解,可以直接理解成最短路
具体细节看代码
#include<cstdio>
#include<queue>
#include<cstring>
const int MAXN = 3e6;
char str[MAXN];
int n,data[10],f[22];
bool inq[22];
void solve(int a,int b){
data[1]=a;data[2]=b;
for(int i=1;i<10;i++)
f[i]=0x3f3f3f3f;
f[0]=0x3f3f3f3f;
for(int i=0;i<10;i++)
inq[i]=0;
std::queue<int> Q;
Q.push(a);Q.push(b);
f[a]=f[b]=1;
inq[a]=1;inq[b]=1;
while(Q.size()){
int cur=Q.front();
Q.pop();
inq[cur]=0;
for(int i=1;i<=2;i++){
int t=(cur+data[i])%10;
if(f[t]>f[cur]+1){
f[t]=f[cur]+1;
if(!inq[t]){
inq[t]=1;
Q.push(t);
}
}
}
}
}
typedef long long ll;
ll ans[22][22];
int main(){
scanf("%s",str+1);
n=strlen(str+1);
for(int i=1;i<=n;i++)
str[i]-='0';
for(int i=0;i<10;i++){
for(int j=0;j<10;j++){
ll tmp=0;
solve(i,j);
int t=0;
for(int k=2;k<=n;k++){
t=(str[k]-t+10)%10;
if(f[t]==0x3f3f3f3f){
tmp=-1;
break;
}
tmp += f[t]-1;
t=str[k];
}
ans[i][j]=tmp;
}
}
for(int i=0;i<10;i++){
for(int j=0;j<10;j++){
printf("%lld ",ans[i][j]);
}
puts("");
}
return 0;
}
C. You Are Given a WASD-string…
Solution
模拟题……
如果不考虑新添加的那个字符的话。
答案只跟运动中的
x
x
x和
y
y
y的极大极小值有关。
为了方便描述,现在把这
4
4
4个量合称运动空间。
如果可以处理出前缀和后缀运动空间,那么就可以枚举在哪个位置放哪个字符,计算答案。
其实前缀是不需要保存的,因为可以在枚举过程中得到。
主要考虑后缀怎么处理,和如何把前缀和后缀合起来。
其实这都属于“如何把前缀和后缀合起来”这个问题。(处理后缀相当于这一步的运动空间和之后的运动空间的合成
假设我们已知前缀和后缀的运动空间和现在点位置在
(
x
,
y
)
(x,y)
(x,y)
那么只需要把后缀运动空间的4个量分别
+
x
+x
+x或
+
y
+y
+y,然后和前缀大的取大,小的取小,这实际上就是坐标系的变换。
然后就做完了……
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long ll;
const int MAXN = 3e5;
char str[MAXN];
int n;
struct E{
int mxx,mnx,mxy,mny;
void init(){
mxx=0;
mnx=0;
mxy=0;
mny=0;
}
void update(int x,int y){
mxx=max(mxx,x); mnx=min(mnx,x);
mxy=max(mxy,y); mny=min(mny,y);
}
E operator ^ (E b){
E ret;
ret.mxx = max(mxx,b.mxx);
ret.mnx = min(mnx,b.mnx);
ret.mxy = max(mxy,b.mxy);
ret.mny = min(mny,b.mny);
return ret;
}
ll getans(){
ll ans =0;
ans = ((ll)(mxx-mnx+1)) * ((ll)(mxy-mny+1));
return ans;
}
E add(int x,int y){
E ret = *this;
ret.mxx+=x; ret.mnx+=x;
ret.mxy+=y; ret.mny+=y;
return ret;
}
E trans(int x,int y){
E ret = (*this);
ret.update(x,y);
return ret;
}
};
E pre[MAXN],suf[MAXN];
void solve(){
scanf("%s",str+1);
n=strlen(str+1);
int x=0,y=0;
for(int i=0;i<=n+1;i++){
pre[i].init();
suf[i].init();
}
for(int i=1;i<=n;i++){
if(str[i]=='W'){x++;}else if(str[i]=='S'){x--;}else if(str[i]=='A'){y--;}else{y++;}
pre[i] = pre[i-1];
pre[i].update(x,y);
}
for(int i=n;i>=1;i--){
x=y=0;
if(str[i]=='W'){x++;}else if(str[i]=='S'){x--;}else if(str[i]=='A'){y--;}else{y++;}
suf[i].update(x,y);
E tmp = suf[i+1].add(x,y);
suf[i] = suf[i] ^ tmp;
}
ll ans = 0x7fffffffffffffff;
x=y=0;
ans = min(ans,suf[1].getans());
for(int i=1;i<n;i++){
if(str[i]=='W'){x++;}else if(str[i]=='S'){x--;}else if(str[i]=='A'){y--;}else{y++;}
E tmp=suf[i+1]; E cur;
cur = pre[i].trans(x+1,y) ^ tmp.add(x+1,y); ans = min(ans,cur.getans());
cur = pre[i].trans(x-1,y) ^ tmp.add(x-1,y); ans = min(ans,cur.getans());
cur = pre[i].trans(x,y-1) ^ tmp.add(x,y-1); ans = min(ans,cur.getans());
cur = pre[i].trans(x,y+1) ^ tmp.add(x,y+1); ans = min(ans,cur.getans());
}
printf("%lld\n",ans);
}
int main(){
int T;
scanf("%d",&T);
while(T--)
solve();
return 0;
}
D. Print a 1337-string…
Solution
先考虑
133333...33337
133333...33337
133333...33337这样情况假设其中有
m
m
m个
3
3
3那么对答案的贡献就是
C
m
2
C_{m}^{2}
Cm2
贪心地让
m
m
m取到尽可能大的值。
剩下的考虑如果再在第二个
3
3
3后面加
n
n
n个
7
7
7那么对答案的贡献就是
n
n
n。
于是剩下的就在第二个
3
3
3后面填上相应多个
7
7
7就好了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve(){ ll n;
scanf("%lld",&n);
printf("133");
ll i;
for(i=2;i;i++){
if((i*(i+1)/2) > n){
break;
}
}
n -= (i-1)*i/2;
for(int j=1;j<=n;j++)putchar('7');
for(int j=3;j<=i;j++)putchar('3');
puts("7");
ll tmp = 0;
tmp += 3 + n + i - 1;
}
int main(){
int T;
scanf("%d",&T);
while(T--)
solve();
return 0;
}
E. You Are Given Some Strings…
Solution
做这个题的时候被误导了一下,于是弱智的我写的后缀数组……
看看有时间再补个其他正常做法吧……
SA 做法
如果只考虑一个字符串匹配的话,可以想到把所有的字符串用特殊的字符连接然后建
S
A
SA
SA
枚举每一个要匹配的字符串,在
S
A
SA
SA数组上左右二分(这里需要
h
e
i
g
h
t
height
height数组,
s
t
表
st表
st表),在二分出的区间上打上
+
1
+1
+1的标记(需要差分)。
打完标记之后扫一遍就可以处理出以主串每个位置开头就多少个匹配。
然后这个题要两个字符串拼起来,那就把每个字符串包括主串分别反过来按上面的套路连起来。
就可以处理出以主串每个位置结尾有多少匹配。
相邻的开头结尾乘一乘就好了
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
typedef long long ll;
const int MAXN = 7e5;
const int MAXM = 2e5+10;
struct ele{
int id,x,y;
};
int Bin[MAXN];
void sorty(ele *s,ele *bs,int len){
rep(i,0,6e5+10)Bin[i]=0;
rep(i,1,len)Bin[s[i].y]++;
rep(i,1,6e5+10)Bin[i]+=Bin[i-1];
dep(i,len,1)bs[Bin[s[i].y]--]=s[i];
rep(i,1,len)s[i]=bs[i];
}
void sortx(ele *s,ele *bs,int len){
rep(i,0,6e5+10)Bin[i]=0;
rep(i,1,len)Bin[s[i].x]++;
rep(i,1,6e5+10)Bin[i]+=Bin[i-1];
dep(i,len,1)bs[Bin[s[i].x]--]=s[i];
rep(i,1,len)s[i]=bs[i];
}
int count(ele *s,int len){
ele tmp=s[1];
int ret=0;
s[1].x=++ret;
rep(i,2,len){
if(tmp.x==s[i].x&&tmp.y==s[i].y){
tmp=s[i];
s[i].x=ret;
}else{
tmp=s[i];
s[i].x=++ret;
}
}
return ret;
}
void sortid(ele *s,ele *bs,int len){
rep(i,0,6e5+10)Bin[i]=0;
rep(i,1,len)Bin[s[i].id]++;
rep(i,1,6e5+10)Bin[i]+=Bin[i-1];
dep(i,len,1)bs[Bin[s[i].id]--]=s[i];
rep(i,1,len)s[i]=bs[i];
}
void build(int *s,int len,int *h,int *rk,int *sa){
static ele SA[MAXN],BUF[MAXN];
int k=1;
rep(i,1,len){SA[i].id=i;SA[i].x=s[i];SA[i].y=0;}
while(k<len){
rep(i,1,len){
if(i+k<=len){SA[i].y=SA[i+k].x;}
else{SA[i].y=0;}
}
sorty(SA,BUF,len);
sortx(SA,BUF,len);
int tmp=count(SA,len);
sortid(SA,BUF,len);
if(tmp == len)
break;
k<<=1;
}
rep(i,1,len){rk[i]=SA[i].x;sa[SA[i].x]=i;}
k=0;
rep(i,1,len){
if(rk[i]==1)continue;
int j=sa[rk[i]-1];
while(i+k<=len&&j+k<=len&&s[i+k]==s[j+k])k++;
h[rk[i]]=k;
if(k>0)k--;
}
}
int n,tot,pos[MAXM],alpha,len,str1[MAXN],str2[MAXN],length[MAXN];
char buf[MAXM];
int f[MAXN],sa[MAXN],rk[MAXN],g[MAXN],g2[MAXN],h[MAXN],rmq[21][MAXN];
int getrmq(int l,int r){
int t=(int)(log(r-l+1)/log(2));
return min(rmq[t][l],rmq[t][r-(1<<t)+1]);
}
int main(){
alpha='z'+2;
scanf("%s",buf+1);
len=strlen(buf+1);
rep(i,1,len)str1[i]=str2[len-i+1]=(int)buf[i];
tot=len+1;
str1[tot]=str2[tot]=++alpha;
scanf("%d",&n);
rep(i,1,n){
pos[i]=tot+1;
scanf("%s",buf+1);
int curLen=strlen(buf+1);
length[i]=curLen;
rep(j,1,curLen){
str1[++tot]=(int)buf[j];
str2[tot]=(int)buf[curLen-j+1];
}
str1[++tot]=++alpha;
str2[tot]=alpha;
}
build(str1,tot,h,rk,sa);
rep(i,1,tot)rmq[0][i]=h[i];
rep(i,1,20)
rep(j,1,tot){
if(j+(1<<i)-1>tot)break;
rmq[i][j]=min(rmq[i-1][j],rmq[i-1][j+(1<<(i-1))]);
}
rep(i,1,n){
int p=pos[i];
p=rk[p];
int l=1,r=p-1,cur_ans=-1;
while(l<=r){
int mid=(l+r)>>1;
if(getrmq(mid+1,p)>=length[i]){
cur_ans=mid;
r=mid-1;
}else{
l=mid+1;
}
}
if(cur_ans==-1)
cur_ans=p;
f[cur_ans]++;
l=p+1;r=tot;cur_ans=-1;
while(l<=r){
int mid=(l+r)>>1;
if(getrmq(p+1,mid)>=length[i]){
cur_ans=mid;
l=mid+1;
}else{
r=mid-1;
}
}
if(cur_ans==-1)
cur_ans=p;
f[cur_ans+1]--;
}
int tmp=0;
rep(i,1,tot){
tmp += f[i];
f[i]=0;
if(sa[i]<=len){
g[sa[i]]=tmp;
}
}
build(str2,tot,h,rk,sa);
rep(i,1,tot)rmq[0][i]=h[i];
rep(i,1,20)rep(j,1,tot){
if(j+(1<<i)-1>tot)break;
rmq[i][j]=min(rmq[i-1][j],rmq[i-1][j+(1<<(i-1))]);
}
rep(i,1,n){
int p=pos[i];
p=rk[p];
int l=1,r=p-1,cur_ans=-1;
while(l<=r){
int mid=(l+r)>>1;
if(getrmq(mid+1,p)>=length[i]){
cur_ans=mid;
r=mid-1;
}else{
l=mid+1;
}
}
if(cur_ans==-1)
cur_ans=p;
f[cur_ans]++;
l=p+1;r=tot;cur_ans=-1;
while(l<=r){
int mid=(l+r)>>1;
if(getrmq(p+1,mid)>=length[i]){
cur_ans=mid;
l=mid+1;
}else{
r=mid-1;
}
}
if(cur_ans==-1)
cur_ans=p;
f[cur_ans+1]--;
}
tmp=0;
rep(i,1,tot){
tmp += f[i];
f[i]=0;
if(sa[i]<=len){
g2[len-sa[i]+1]=tmp;
}
}
ll ans=0;
rep(i,1,len-1){
ans+=(ll)g2[i]*g[i+1];
}
printf("%lld",ans);
return 0;
}