费劲千辛万苦,终于自己独立做出了一场 A G C AGC AGC,尽管蒟蒻总共花费了近 5 h 5h 5h,不过依旧很开心,今天晚上可以奖励自己吃点宵夜。
A - Ice Tea Store
按照 2 L 2L 2L的容量排个序,如果最小的是 2 L 2L 2L装的,那么就先拿 2 L 2L 2L装,再拿第二小的装剩下的,否则直接拿最小的装。
#include<bits/stdc++.h>
using namespace std;
pair<int,int> e[4];
int a,b,c,d,n;
int main(){
scanf("%d %d %d %d",&a,&b,&c,&d);
scanf("%d",&n);
e[0].first=a*8;e[0].second=0;
e[1].first=b*4;e[1].second=1;
e[2].first=c*2;e[2].second=2;
e[3].first=d;e[3].second=3;
sort(e,e+4);
if(e[0].second<=2) printf("%lld\n",1ll*n*e[0].first/2);
else printf("%lld\n",1ll*(n/2)*e[0].first+(n%2)*e[1].first/2);
}
B - Reverse and Compare
与其他不同当且仅当区间端点的两个字符不同。
若端点的两个字符相同,那么与交换
[
l
+
1
,
r
−
1
]
[l+1,r-1]
[l+1,r−1]相同。
端点不同显然与其他不同。
#include<bits/stdc++.h>
using namespace std;
const int N=200010;
char s[N];
int n,t[26];
int main(){
scanf("%s",s+1);n=strlen(s+1);
long long ans=0;
for(int i=1;i<=n;i++) ans+=i-1-t[s[i]-'a'],t[s[i]-'a']++;
printf("%lld\n",ans+1);
}
C - Fountain Walk
很容易想到最长上升子序列,事实上就是这样。
手玩很久才发现,最后如果最大长度等于宽度或者长度,答案要加一个半圆。
充要性显然。
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define mk make_pair
using namespace std;
const int N=200010;
int a,b,c,d,n,m,f[N];
const double Pi=acos(-1.0);
pii p[N];
int main(){
scanf("%d %d %d %d",&a,&b,&c,&d);
if(c<a){swap(a,c);swap(b,d);}
scanf("%d",&n);
int x,y;
for(int i=1;i<=n;i++){
scanf("%d %d",&x,&y);
if(a<=x && x<=c && min(b,d)<=y && y<=max(b,d))
p[++m]=mk(x,y);
}
sort(p+1,p+1+m);
if(a==c || b==d){
printf("%.15lf\n",100.0*(abs(c-a)+abs(b-d))+(m?10*Pi-20:0));
return 0;
}
if(b>d) reverse(p+1,p+1+m);
int mmax=0;f[0]=-1;
for(int i=1;i<=m;i++){
int l=0,r=mmax,ans=0;
while(l<=r){
int mid=(l+r)/2;
if(p[i].second>f[mid]) l=(ans=mid)+1;
else r=mid-1;
}
ans++;
if(ans==mmax+1) f[ans]=p[i].second,mmax++;
else f[ans]=min(f[ans],p[i].second);
}
printf("%.15lf\n",100.0*(abs(c-a)+abs(b-d))+mmax*(Pi*5-20)+(mmax==c-a+1 || mmax==abs(b-d)+1)*(Pi*5));
}
D - Shift and Flip
枚举最后一步是什么,再枚举最后实际上转了多少位。
如果是向左转,一些点可能向右转一些,反转之后再向左转回来。
可以给他们按向右转的次数排个序,然后枚举一下就可以了。
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define mk make_pair
using namespace std;
const int N=2010;
char a[N],b[N];
int n,l[N],r[N],pre[N];
pii p[N];
int main(){
scanf("%s",a+1);n=strlen(a+1);
scanf("%s",b+1);
int las=1e9;
for(int i=1;i<=n;i++){
las++;
if(b[i]=='1') las=0;
l[i]=las;
}
if(las>=1e9){
int ans=1e9;
for(int j=0;j<n;j++){
bool tf=true;
for(int i=1;i<=n;i++) if(a[i]!=b[(i+j-1)%n+1]){
tf=false;break;
}
if(tf) ans=min(ans,min(j,n-j));
}
if(ans==1e9) printf("-1\n");
else printf("%d\n",ans);
return 0;
}
for(int i=1;i<=n;i++){
las++;
if(b[i]=='1') las=0;
l[i]=las;
}las=1e9;
for(int i=n;i>=1;i--){
las++;
if(b[i]=='1') las=0;
r[i]=las;
}
for(int i=n;i>=1;i--){
las++;
if(b[i]=='1') las=0;
r[i]=las;
}
int ans=1e9;
for(int i=0;i<=n;i++){
int tot=0,m=0;
for(int j=1;j<=n;j++){
int nex=(j+i-1)%n+1;
if(a[j]!=b[nex]){
if(a[j]=='1') p[++m]=mk(r[j],l[j]);
tot++;
}
}
sort(p+1,p+1+m);pre[m+1]=0;
for(int j=m;j>=1;j--) pre[j]=max(pre[j+1],p[j].second);
for(int j=0;j<=m;j++) ans=min(ans,tot+pre[j+1]*2+max(i,p[j].first)*2-i);
}
for(int i=0;i<=n;i++){
int tot=0,m=0;
for(int j=1;j<=n;j++){
int nex=(j+n-i-1)%n+1;
if(a[j]!=b[nex]){
if(a[j]=='1') p[++m]=mk(l[j],r[j]);
tot++;
}
}
sort(p+1,p+1+m);pre[m+1]=0;
for(int j=m;j>=1;j--) pre[j]=max(pre[j+1],p[j].second);
for(int j=0;j<=m;j++) ans=min(ans,tot+pre[j+1]*2+max(i,p[j].first)*2-i);
}
printf("%d\n",ans);
}
E - Shuffle and Swap
比较蠢,想了1.5h
实际上考虑将
n
n
n个点按照
(
A
i
,
B
i
)
(A_i,B_i)
(Ai,Bi)划分成四类
(
0
,
1
)
,
(
1
,
0
)
,
(
1
,
1
)
,
(
0
,
0
)
(0,1),(1,0),(1,1),(0,0)
(0,1),(1,0),(1,1),(0,0),分别设置为
X
,
Y
,
Z
X,Y,Z
X,Y,Z类,最后一类直接扔掉。
两两交换肯定形成了一个置换环。将置换环写成链的形状,容易发现,一定是
Y
Z
.
.
.
Z
X
YZ...ZX
YZ...ZX。中间的
Z
Z
Z有多少个无所谓。
那么最后的序列实际上就是许多这样的链和一堆
Z
Z
Z胡乱交换,我们先考虑比较困难的前面这一部分。
下面用
(
X
,
Y
)
(X,Y)
(X,Y)来表示将
X
,
Y
X,Y
X,Y交换,序列指的是最后的交换序列,
P
P
P是
Y
Y
Y或
Z
Z
Z。
考虑
D
p
Dp
Dp来计算这样的方案数就好了。
设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示当前放了
i
i
i个
Z
Z
Z,
j
j
j条链的方案数。
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
∗
j
+
f
[
i
]
[
j
−
1
]
f[i][j]=f[i-1][j]*j+f[i][j-1]
f[i][j]=f[i−1][j]∗j+f[i][j−1]
f
[
i
−
1
]
[
j
]
∗
j
f[i-1][j]*j
f[i−1][j]∗j表示在序列前面的位置中将某个
(
P
,
X
)
(P,X)
(P,X),替换成
(
P
,
Z
)
(P,Z)
(P,Z),并在当前这一位放上
(
Z
,
X
)
(Z,X)
(Z,X)。我们按照第一次出现的顺序给每一个
Z
Z
Z编号,所以就有了顺序,乘上一个系数。
f
[
i
]
[
j
−
1
]
f[i][j-1]
f[i][j−1]表示直接在当前位置上放一个
(
Y
,
X
)
(Y,X)
(Y,X)。
后面直接枚举剩下多少个
Z
Z
Z就可以了,他们可以随便交换,乘上一些组合数把他们插到序列里去就好。
发现其实
f
f
f很眼熟,没错,他就是
S
(
i
+
j
,
j
)
S(i+j,j)
S(i+j,j)
#include<bits/stdc++.h>
using namespace std;
const int N=10010,mod=998244353;
char a[N],b[N];
int fac[20010],inv[20010],f[10001][10001];
int n;
int ksm(int x,int t){
int tot=1;
while(t){
if(t&1) tot=1ll*tot*x%mod;
x=1ll*x*x%mod;
t/=2;
}
return tot;
}
void ad(int&x,int y){x=(x+y>=mod)?(x+y-mod):(x+y);}
int C(int x,int y){return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;}
int main(){
scanf("%s",a+1);n=strlen(a+1);
scanf("%s",b+1);
int ta=0,tb=0;
for(int i=1;i<=n;i++) if(a[i]=='1'){
if(b[i]=='1') ta++;
else tb++;
}
fac[0]=1;for(int i=1;i<=20000;i++) fac[i]=1ll*fac[i-1]*i%mod;
inv[20000]=ksm(fac[20000],mod-2);for(int i=19999;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
f[0][0]=1;
for(int i=0;i<=ta;i++)
for(int j=0;j<=tb;j++){
if(i) f[i][j]=1ll*f[i-1][j]*j%mod;
if(j) ad(f[i][j],f[i][j-1]);
}
int ans=0;
for(int i=0;i<=ta;i++)
ans=(ans+1ll*f[i][tb]*fac[i]%mod*C(ta+tb,ta-i)%mod*C(ta,i)%mod*fac[ta-i]%mod*fac[ta-i])%mod;
ans=1ll*ans*fac[tb]%mod*fac[tb]%mod;
printf("%d\n",ans);
}
F - Yes or No
感觉比
E
E
E题简单一些,想了
1
h
1h
1h。
显然谁剩的多猜谁,将剩下的差值设为
A
A
A。
考虑一条折线,
(
i
,
A
i
)
(i,A_i)
(i,Ai)表示第
i
i
i次的
A
A
A为
A
i
A_i
Ai。
最后肯定是
(
n
+
m
,
0
)
(n+m,0)
(n+m,0)。
若
A
A
A不为
0
0
0,往
y
=
0
y=0
y=0靠近的时候
a
n
s
+
+
ans++
ans++,远离的时候不变,这一部分的贡献是
max
(
n
,
m
)
\text{max}(n,m)
max(n,m)
若当前
A
=
0
A=0
A=0,那么下一步盲猜,有
1
/
2
1/2
1/2的几率猜中,这一部分的贡献可以将经过
y
=
0
y=0
y=0的次数期望算出来,乘上
1
/
2
1/2
1/2。
对于每一个点单独计算概率即可,简单的组合数学。
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353,N=1000010;
int n,m,fac[N],inv[N];
int ksm(int x,int t){
int tot=1;
while(t){
if(t&1) tot=1ll*tot*x%mod;
x=1ll*x*x%mod;
t/=2;
}
return tot;
}
int C(int x,int y){return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;}
int main(){
scanf("%d %d",&n,&m);
if(m<n) swap(n,m);
fac[0]=1;for(int i=1;i<N;i++) fac[i]=1ll*fac[i-1]*i%mod;
inv[N-1]=ksm(fac[N-1],mod-2);for(int i=N-2;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
int ans=0;
for(int i=0;i<n+m;i++) if((i&1)==((m-n)&1) && m-n<=i)
ans=(ans+1ll*C(i,(i-(m-n))/2)*C(n+m-i,n-(i-(m-n))/2))%mod;
ans=1ll*(mod+1)/2*ans%mod*ksm(C(n+m,n),mod-2)%mod;
printf("%d\n",(ans+m))%mod;
}