某古 11 月月賽 I 遊記
某古 11 月月賽 I 遊記
難度好評,沒有像我上次打的那場比賽那麼水了,不過自己的分數還是好低,只會前三題。。。
希望你古月賽的題目一直都能像這場這麼有意思。
A 「MCOI-03」正方
題目分析
三角形面積公式 \(S=\frac{1}{2}ah\) ,由於 \(a\) 相等,所以題目給出的其實就是 \(h\) 之比。
題目中給出了 \(a,b,c,d\) ,不妨令 \(a\le b\le c\le d\) ,由於題目給出的是正方形,所以必須要滿足 \(a+d=b+c\) ,否則不是正方形,答案就是 \(0\) ,如果滿足 \(a+d=b+c\) ,不妨令正方形的左下角為 \((0,0)\) ,正方形邊長為 \(a+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('\n'); } } 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('\n'); } 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-1\choose k-1}\times {n-r+k-1\choose k-1}\) ,不妨設 \(S(l,r)\) 的 0 級偏值等於 \(T(l,r)\) ,那麼答案就是 \(\sum_{l=1}^n\sum_{r=l}^n(T(l,r)\times {l-1+k-1\choose k-1}\times {n-r+k-1\choose k-1})\) 。
如何統計所有的 \(S(l,r)\) ?考慮從右邊往左邊掃,由 \(\sum_{r=l+1}^n(T(l+1,r)\times {n-r+k-1\choose k-1})\) 遞推得出 \(\sum_{r=l}^n(T(l,r)\times {n-r+k-1\choose k-1})\) 。
首先需要知道,如果固定左端點,隨著右端點的增加,不斷刪去匹配括號得到的串 )))...))((...(((
中的左半部分(即 )))...))
)的長度一定會不斷增加,那麼我們刪去匹配括號最終保留的串肯定是這樣的:
)))(...((
)))(...((
)))(...((
))(...((
))(...((
))(...((
)(...((
(...((
(...((
.
.
.
(
假如左邊添加了一個 )
:
))))(...((
))))(...((
))))(...((
)))(...((
)))(...((
)))(...((
))(...((
)(...((
)(...((
.
.
.
)(
)
只是每個串的長度增加了 \(1\) ,所以此時滿足:
\[\sum_{r=l}^n(T(l,r)\times {n-r+k-1\choose k-1})=\sum_{r=l}^n{n-r+k-1\choose k-1}+\sum_{r=l+1}^n(T(l+1,r)\times {n-r+k-1\choose k-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('\n');
}
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('\n');
}
return 0;
}
總結
這次月賽沒有打多久,這個成績勉強滿意吧。