AtCoder Regular Contest 109
為什麼還沒有 Official Editorial 啊……哦,原來是日文題解,那沒事了。
A - Hands
有兩幢 100 層的樓房 \(A,B\) ,將地面所在的樓層稱為第一層樓。
\(\forall i\in[1,100],i\in Z\) ,\(A,B\) 的第 \(i\) 層有樓梯相連,通過時間為 \(x\) ; \(\forall i\in[1,99]\) ,\(A\) 的 \(i+1\) 層和 \(B\) 的第 \(i\) 層有樓梯相連,通過時間為 \(x\) .這些樓梯都是雙向通行的。
\(A,B\) 樓內有直升的樓梯,第 \(i\) 層到第 \(i+1\) 層之間的通行時間為 \(y\)
求從 \(B\) 的第 \(b\) 層到 \(A\) 的第 \(a\) 層的最短時間。
Thoughts & Solution
分討題。
A的高層到B的低層 :如果 \(A_{i+1}\to B_i\to A_i\) (也就是 \(2x\) 的代價)不大於 \(A_{i+1}\to A_i\) (也就是 \(y\) 的代價),那麼走到平層之後再到 \(B\) 即可,特判 \(A_{i+1}\to B_i\) 的情況。如果大於,那麼就從 \(A\) 通過直升樓梯下去,走到平層之後再到 \(B\) .
A到B平層 :直接走過去。代價為 \(x\) .
B的高層到A的低層 :如果 \(B_{i+1}\to A_{i+1}\to B_i\)
//Author: RingweEH int a,b,x,y; int main() { a=read(); b=read(); x=read(); y=read(); int ans=0; if ( a>b ) { if ( 2*x<=y ) { int del=a-b; if ( del==1 ) ans=x; else ans=x+(del-1)*2*x; } else ans=(a-b-1)*y+x; } if ( a==b ) ans=x; if ( a<b ) { if ( 2*x<=y ) { int del=b-a; if ( del==1 ) ans=3*x; else ans=3*x+2*x*(del-1); } else ans=(b-a)*y+x; } printf( "%d",ans ); return 0; }
B - Log
需要 \(n\) 段長度分別為 \(1,2,\dots n\) 的木條。現在有 \(n+1\) 個木條,長度為 \(1,2,\dots ,n+1\) ,每個代價為 1 ,買了之後可以砍任意次數,並扔掉不需要的部分。問滿足需求的最低金額。
Thoughts & Solution
貪心取 \(n+1\) 並分成 \(1,2,\dots ,k\) ,二分即可。分完之後剩下的就從完整的裡面直接拿。
注意二分上界不要設太大,不然就爆 long long 了(畢竟你要算 \(1\sim n\) 的和啊)。
//Author: RingweEH
ll n;
int main()
{
scanf( "%lld",&n );
ll l=1,r=2e9,ans=-1;
while ( l<=r )
{
ll mid=(l+r)>>1;
if ( (mid*(mid+1)/2)<=(n+1) ) l=mid+1,ans=mid;
else r=mid-1;
}
printf( "%lld\n",n-ans+1 );
return 0;
}
C - Large RPS Tournament
有 \(2^k\) 個人,從 \(0\sim 2^k-1\) 標號。給定一個長度為 \(n\) 的字串 \(S\) ,R
表示石頭,P
表示布,S
表示剪刀。第 \(i\) 個人只會出 \((i\bmod n+1)\) 的手勢。兩兩晉級決定最後的 winner 。求勝者的手勢。
Thoughts & Solution
這裡有個極簡做法。
看到題目就很容易想到倍增吧。然後直接每次將 \(S\) 複製一遍,兩兩算出勝者,然後作為新的 \(S\) ,這樣做 \(k\) 次,然後取頭就是答案了。
自己模擬一下很容易就理解為什麼是對的吧。
//Author: RingweEH
ll n,k;
char win[230][230];
string str,dbstr;
int main()
{
n=read(); k=read(); cin>>str;
win['R']['R']=win['R']['S']=win['S']['R']='R';
win['S']['S']=win['S']['P']=win['P']['S']='S';
win['P']['P']=win['P']['R']=win['R']['P']='P';
while ( k-- )
{
dbstr=str+str;
for ( int i=0; i<n; i++ )
str[i]=win[dbstr[i*2]][dbstr[i*2+1]];
}
cout<<str[0];
return 0;
}
D - L
二維平面上一開始有三個點 \((0,0),(0,1),(1,0)\) 形成 \(L\) 形,每次操作可以改變一個點的位置使得仍然是 \(L\) 形。給出終止位置,求最小的移動步數。\(|x|,|y|\leq 1e9,T\leq 1e3\) .
Thoughts & Solution
相當於是對於每個 L
形,找到一個唯一對應的點(題解裡說是重心,但是個人感覺更像是 將這個 L 形所在的 \(2\times 2\) 的矩形作為一個整體,這個整體裡面有 4 個點,分別對應 L 形拐角所在位置的 4 種情況 ,建立這樣的一種點對點的對應關係),然後觀察這個點隨著 L 形移動的變化,會有 7 種方向可以走,然後就相當於二維平面上移動一個點,算一下然後再加上 “正好在不能走的位置上”的代價即可。
//Author: RingweEH
pair<int,int> node[3];
ll nowX,nowY;
void turning()
{
sort( node,node+3 ); pair<int,int> mn=node[1];
for ( int i=0; i<3; i++ ) //左下角
mn.first=min( mn.first,node[i].first ),mn.second=min( mn.second,node[i].second );
nowX=mn.first*2ll; nowY=mn.second*2ll;
if ( node[0].second==node[2].second && node[1].first==node[0].first ) return;//對應點在左下角
if ( node[0].first+1==node[2].first && node[0].second+1==node[2].second )
{
if ( node[1].first==node[0].first ) nowY++; //對應點在左上
else nowX++; //右下
return;
}
nowX++; nowY++; //右上
}
int main()
{
int T=read();
while ( T-- )
{
for ( int i=0; i<3; i++ )
node[i].first=read(),node[i].second=read();
turning(); ll ans=max( abs(nowX),abs(nowY) );
if ( nowX==nowY && nowX && (nowX^1) ) ans++;
printf( "%lld\n",ans );
}
}
E - 1D Reversi Builder
有一排 \(n\) 個格子。\(A\) 先將每個格子等概率塗成黑色或者白色,設每個格子的顏色為 \(c_i\) 。
最開始,先在格子 \(S\) 上放一個和 \(c_S\) 同色的石子。
\(A,B\) 將按照以下步驟進行遊戲,\(A\) 先手:
- 選一個沒有石子的格子 \(i\) 並放一個和格子同色的石子。
- 尋找一個離 \(i\) 最近且放了和 \(i\) 同色的石子的格子 \(j\) ,並將 \(i,j\) 之間所有格子的石子顏色變成 \(i\) 的石子顏色。
當沒有空的格子時,遊戲結束。
\(A\) 要最大化最終的黑色石子個數,\(B\) 則最大化白色。對於所有的 \(S=1,2,\dots,n\) ,求最終黑色石子個數的期望。
答案對 \(998244353\) 取模,\(1\leq n\leq 2e5\) .
Thoughts & Solution
有結論:連續段內不會出現 黑-白-黑 的情況 。
如果出現了,設兩個黑色的位置從左到右是 \(x,y\) (假定 \(x\) 先放)。
那麼 \(y\) 右邊一定有 \(y'\) 否則就會和 \(x\) 配對,而去掉 \(y\) 後剩下 \(x,y'\) 。
這樣歸納下去,一定是有一次不能再推了,就出現了矛盾。所以這樣的情形是不可能出現的。
同理也可以說明 白-黑-白 的情形不會出現。
那麼就可以得到,每一次操作之後,除了沒有放石子的格子,其他格子的石子排布一定形成了兩個顏色相同的連續段。
現在就可以對序列兩端的顏色進行討論了。
- 如果 \(c_1=c_n\) ,由結論,顯然可以知道最後的局面一定全是端點顏色。
- 如果 \(c_1\neq c_n\) ,不妨設 \(c_1=black,c_n=white\) .令 \(x\) 為最靠左端的白色塊位置,\(y\) 為最靠右端的黑色塊位置。顯然,這樣答案在最壞情況下的上下界是 \([1,x)\) ,最好情況下是 \([1,y]\) .現在就在於 \(x-1\) 和 \(y+1\) 取的順序了。
- 如果 \(x-1\) 先取,那麼當選了 \(y\) 之後,\([1,y]\) 必然都是黑色且不會再改變,達到上限。因此,如果 \(x-1\) 到 \(S\) 的距離不大於 \(S\) 到 \(y+1\) 的距離,那麼答案就是 \([1,y]\) .
- 如果 \(y+1\) 先取,類似地有 \([x,n]\) 為白色,取到下限。因此,如果 \(x-1\) 到 \(S\) 的距離大於 \(S\) 到 \(y+1\) 的距離,答案就是 \([1,x)\) .
按照這個東西算一算就好了。
//Author: RingweEH
const int N=2e5+10;
const ll Mod=998244353;
int n;
ll Inv2,powe2[N*2],f[N],ans[N];
ll power( ll a,ll b )
{
ll res=1;
for ( ; b; b>>=1,a=a*a%Mod )
if ( b&1 ) res=res*a%Mod;
return res;
}
void init()
{
powe2[0]=1;
for ( int i=1; i<=n*2; i++ )
powe2[i]=powe2[i-1]*2%Mod;
}
int main()
{
n=read();
if ( n==1 ) { printf( "%lld\n",power(2,Mod-2) ); return 0; }
init(); ll sum=0; sum=(sum+powe2[n-2]*n)%Mod; sum=(sum+powe2[n-2]*n)%Mod;
for ( int i=1; i<=n; i++ )
ans[i]=sum;
for ( int i=2; i<=n; i++ )
f[i]=(f[i-1]+(2*i-1)*powe2[2*i-3])%Mod;
for ( int i=3; i<=n-2; i++ )
ans[i]=(ans[i]+f[i-max(2*i-n,1)]-f[1]+Mod)%Mod;
ll inv2=power( powe2[n],Mod-2 );
for ( int i=1; i<=n; i++ )
ans[i]=ans[i]*inv2%Mod;
for ( int i=1; i<=n; i++ )
printf( "%lld\n",ans[i] );
return 0;
}
Magic
這裡有一份來自 @hehezhou 的奇妙遞推程式碼……呵姥爺yyds!
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
inline int power(int a, int b) {
long long res = a, ans = 1;
for (; b; b >>= 1, res = res * res % mod) if (b & 1) ans = ans * res % mod;
return ans;
}
int ans[200010];
int main() {
ans[1] = ans[2] = 0, ans[3] = 6, ans[4] = 46;
int n;
scanf("%d", &n);
for (int i = 5; i <= n; i++) {
ans[i] = (9ll * ans[i - 1] + 998244329ll * ans[i - 2] + 16ll * ans[i - 3]) % mod;
}
int qwq = 1ll * power(2, mod - 2) * n % mod;
for (int i = 1; i <= n; i++) {
int cur = 1ll * ans[min(i, n + 1 - i)] * power(2, mod - 1 - n) % mod;
printf("%d\n", (qwq + cur) % mod);
}
}
F - 1D Kingdom Builder
有一行格子,其中小於 \(0\) 的為白色,大於 \(n\) 的為黑色,中間的格子顏色給定。一些格子需要被標記,按以下規則進行:
- 選擇一個顏色 \(col\) ,找到一個未標記的、旁邊有標記點的、顏色為 \(col\) 的格子,並標記
- 如果找不到,就找任意一個顏色為 \(col\) 的格子
求標記完所有格子的最少操作次數。 \(n\leq 1e5\) .
Thoughts & Solution
Warning:屬於 【題單】最近遇見的 SHIT DP題 三連 ,題單 適合用於自虐和消磨時間 ,請謹慎考慮自身生命安全
Unfinished.