某古 11 月月赛 I 游记
难度好评,没有像我上次打的那场比赛那么水了,不过自己的分数还是好低,只会前三题。。。
希望你古月赛的题目一直都能像这场这么有意思。
A 「MCOI-03」正方
题目分析
三角形面积公式 (S=frac{1}{2}ah) ,由于 (a) 相等,所以题目给出的其实就是 (h) 之比。
题目中给出了 (a,b,c,d) ,不妨令 (ale ble cle d) ,由于题目给出的是正方形,所以必须要满足 (a+d=b+c) ,否则不是正方形,答案就是 (0) ,如果满足 (a+d=b+c) ,不妨令正方形的左下角为 ((0,0)) ,正方形边长为 (a+d) ,那么这个点的选择方法就只有 ((a,b),(a,c),(b,a),(c,a),(d,b),(d,c),(b,d),(c,d)) 八种,排序后去重就行了。
参考代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ch() getchar()
#define pc(x) putchar(x)
template<typename T>inline void read(T&x){
int f;char c;
for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>inline void write(T x){
static char q[64];int cnt=0;
if(!x)pc('0');if(x<0)pc('-'),x=-x;
while(x)q[cnt++]=x%10+'0',x/=10;
while(cnt--)pc(q[cnt]);
}
long long A[4];
pair<long long,long long>s[8];
int main(){
int q;read(q);
while(q--){
for(int i=0;i<4;++i)read(A[i]);sort(A,A+4);
if(A[0]+A[3]!=A[1]+A[2])puts("0");
else{
int cn=0;
for(int i=0;i<=3;i+=3){
for(int j=1;j<=2;j+=1){
s[cn++]=pair<long long,long long>(A[i],A[j]);
s[cn++]=pair<long long,long long>(A[j],A[i]);
}
}
sort(s,s+cn);cn=unique(s,s+cn)-s;write(cn),pc('
');
}
}
return 0;
}
B 「MCOI-03」村国
题目分析
一开始看到这题真的没有头绪,但是仔细思考后发现题面就是唬人的,感觉这样的题目还是挺有意思的。
假如当前好感值最高的点是 (x) ,那么会先选择一直去 (x) 直到 (x) 旁边出现了一个点的好感值和 (x) 相等并且编号尽可能小,不妨设这个点为 (y) ,那么就会一直先去 (x,y) 中编号小的,然后去 (x,y) 中编号大的一直循环,所以此时只需要判断 (m) 的奇偶性就行了。
需要注意的一点就是如果 (x) 不存在相邻的点答案就是 (x) 。
参考代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ch() getchar()
#define pc(x) putchar(x)
template<typename T>inline void read(T&x){
int f;char c;
for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>inline void write(T x){
static char q[64];int cnt=0;
if(!x)pc('0');if(x<0)pc('-'),x=-x;
while(x)q[cnt++]=x%10+'0',x/=10;
while(cnt--)pc(q[cnt]);
}
const int maxn=2000006;
int a[maxn];
int main(){
int T;read(T);
while(T--){
int n;long long m;int po=0;read(n),read(m);
for(int i=1;i<=n;++i){
read(a[i]);if(a[i]>a[po])po=i;
}
int bo=0;
for(int i=1;i<n;++i){
int u,v;read(u),read(v);if(v==po)u^=v^=u^=v;
if(u==po&&(a[v]>a[bo]||(a[v]==a[bo]&&v<bo)))bo=v;
}
if(!bo)write(po);
else{
int delta=a[po]-a[bo];
if(m<delta)write(po);
else{
m-=delta;
if(po>bo)po^=bo^=po^=bo;
if(m&1)write(bo);else write(po);
}
}
pc('
');
}
return 0;
}
C 「MCOI-03」括号
题目分析
当 (k=0) 的时候,可以通过一个栈来求答案,不断删去匹配括号最后留下的串肯定是 )))...))((...(((
,答案就是这个串的长度,下面只考虑 (k>0) 的情况。
考虑 (S(l,r)) 的 0 级偏值给答案造成贡献时需要乘以的系数,要从串 (S(1,n)) 得到 (S(l,r)) 左端点需要向右边移动 (l-1) 次,右端点需要向左边移动 (n-r) 次,这些移动都是在 (k) 步里面完成的,由隔板法就可以得出要乘上的系数就是 ({l-1+k-1choose k-1} imes {n-r+k-1choose k-1}) ,不妨设 (S(l,r)) 的 0 级偏值等于 (T(l,r)) ,那么答案就是 (sum_{l=1}^nsum_{r=l}^n(T(l,r) imes {l-1+k-1choose k-1} imes {n-r+k-1choose k-1})) 。
如何统计所有的 (S(l,r)) ?考虑从右边往左边扫,由 (sum_{r=l+1}^n(T(l+1,r) imes {n-r+k-1choose k-1})) 递推得出 (sum_{r=l}^n(T(l,r) imes {n-r+k-1choose k-1})) 。
首先需要知道,如果固定左端点,随着右端点的增加,不断删去匹配括号得到的串 )))...))((...(((
中的左半部分(即 )))...))
)的长度一定会不断增加,那么我们删去匹配括号最终保留的串肯定是这样的:
)))(...((
)))(...((
)))(...((
))(...((
))(...((
))(...((
)(...((
(...((
(...((
.
.
.
(
假如左边添加了一个 )
:
))))(...((
))))(...((
))))(...((
)))(...((
)))(...((
)))(...((
))(...((
)(...((
)(...((
.
.
.
)(
)
只是每个串的长度增加了 (1) ,所以此时满足:
假如左边添加了一个 (
:
()))(...((
()))(...((
()))(...((
())(...((
())(...((
())(...((
()(...((
((...((
((...((
.
.
.
((
(
此时可能出现匹配括号,需要把匹配括号删去:
)(...((
)(...((
)(...((
(...((
(...((
(...((
(...((
((...((
((...((
.
.
.
((
(
不难发现,所有原本存在 )
的子串的长度都减小了 (2) ,不妨设最小的满足 (S(l+1,r)) 左边保留了一个 )
的 (r) 是 (p) ,那么可以得到:
由此我们可以得出,我们需要维护的就是 )
数量不同的所有断点,不难发现这个是很好维护的,用一个栈维护就行了,时间复杂度 (mathcal O(n+k)) 。
参考代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ch() getchar()
#define pc(x) putchar(x)
template<typename T>inline void read(T&x){
int f;char c;
for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>inline void write(T x){
static char q[64];int cnt=0;
if(!x)pc('0');if(x<0)pc('-'),x=-x;
while(x)q[cnt++]=x%10+'0',x/=10;
while(cnt--)pc(q[cnt]);
}
const int mod=998244353,maxn=2000005;
int mo(const int x){
return x>=mod?x-mod:x;
}
int fac[maxn],iac[maxn];
int binom(int n,int m){
return 1ll*iac[m]*iac[n-m]%mod*fac[n]%mod;
}
char s[maxn];
int st[maxn],tp;
int main(){
int n,k;read(n),read(k);
if(k==0){
scanf("%s",s+1);
int now=0,ans=0;
for(int i=1;i<=n;++i){
if(s[i]==')'){
if(now)--now;
else ++ans;
}
else ++now;
}
ans+=now;
write(ans),pc('
');
}
else{
int mx=n+k;
iac[0]=iac[1]=fac[0]=fac[1]=1;
for(int i=2;i<=mx;++i)
iac[i]=1ll*(mod-mod/i)*iac[mod%i]%mod;
for(int i=2;i<=mx;++i)
fac[i]=1ll*fac[i-1]*i%mod,iac[i]=1ll*iac[i-1]*iac[i]%mod;
scanf("%s",s+1);
int ans=0,sum=0,all=0;
for(int i=n;i>=1;--i){
sum=mo(sum+binom(n-i+k-1,k-1));
all=mo(all+sum);
if(s[i]=='('){
if(tp){
all=mo(mo(mo(mod-st[tp])<<1)+all);--tp;
}
}
else{
++tp;st[tp]=sum;
}
ans=mo(ans+1ll*all*binom(i-1+k-1,k-1)%mod);
}
write(ans),pc('
');
}
return 0;
}
总结
这次月赛没有打多久,这个成绩勉强满意吧。