Atcoder Beginner Contest 327 解题报告
Atcoder Beginner Contest 327
Hints
D
$\quad$这个定义……看起来这么熟悉?E
$\quad$固定 $k$ 试试?F_1
$\quad$扫描线?F_2
$\quad$区间加,区间 $\max$,咋维护?A
直接查找 \(\texttt{ab}\) 和 \(\texttt{ba}\) 即可。
int n;
string s;
void Solve(int CASE)
{
cin>>n>>s;
puts((~s.find("ab"))||(~s.find("ba"))?"Yes":"No");
}
B
\(16^{16}>10^{18}\implies a\le15\)。
ll n;
void Solve(int CASE)
{
cin>>n;
for(int i=1;i<=15;i++)
{
ll pw=1;
for(int j=1;j<=i;j++)pw*=i;
if(pw==n)put_ret(i);//put_ret 就是直接输出然后返回
}
cout<<-1;
}
C
玩过数独吗?
判一下每行每列每宫 \(1\sim9\) 是否都出现一次即可。
int c[10][10],r[10][10],g[10][10];
int G(int x,int y){return (x-1)/3*3+(y-1)/3+1;}
void Solve(int CASE)
{
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
{
int x;cin>>x;
r[i][x]++,c[j][x]++,g[G(i,j)][x]++;
}
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
if(c[i][j]!=1||r[i][j]!=1||g[i][j]!=1)put_ret("No");
cout<<"Yes";
}
D
观察这个 good 的定义,发现其实就是一个 \(n\) 个点的无向图,其中 \(s_i\) 和 \(t_i\) 连边,然后让你判断是否是个二分图。
const int N=200005;
int n,m;
VI g[N];
int col[N],X[N];
bool dfs(int x)
{
for(int y:g[x])
{
if(!~col[y])
{
col[y]=!col[x];
if(!dfs(y))return 0;
}
else if(col[y]==col[x])return 0;
}
return 1;
}
void Solve(int CASE)
{
cin>>n>>m;
for(int i=1;i<=m;i++)cin>>X[i];
for(int i=1;i<=m;i++)
{
int y;cin>>y;
g[X[i]].PB(y),g[y].PB(X[i]);
}
memset(col,-1,sizeof(col));
for(int i=1;i<=n;i++)
if(!~col[i])
{
if(!dfs(i))put_ret("No");
}
cout<<"Yes";
}
E
好像真的挺类似于 atcoder 的 rating 计算。
一看到这么复杂的式子,肯定想 dp。
dp 状态是啥呢,肯定是 \(k\) 固定时某个东西的最大值。
但是这么复杂的式子,感觉没法 dp 啊!
等等,dp 状态是「\(k\) 固定时某个东西的最大值」,并没有说是什么东西。
观察这一坨式子,就能发现其实只有左边分式的分子是与 perf 的具体值有关。
那么考虑 dp 分子,注意到打了某场比赛时
\[\begin{aligned}dp_i&=\max\left\{\sum_{i=1}^k(0.9)^{k-i}Q_i\right\}\\&=\max\left\{\sum_{i=1}^{k-1}\left((0.9)^{k-i}Q_i\right)+Q_k\right\}\\&=\max\left\{0.9\sum_{i=1}^{k-1}\left((0.9)^{k-i-1}Q_i\right)+Q_k\right\}\\&=\max\{0.9dp_{k-1}+Q_k\}\end{aligned},
\]
所以这东西就能转移了。最后枚举 \(k\) 计算一下最终 rating 然后取个最大值即可。
时间复杂度 \(\Theta(n^2)\)。
const int N=5005;
int n,a[N];
double dp[N];
void Solve(int CASE)
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
for(int j=i;j;j--)
dp[j]=max(dp[j],dp[j-1]*0.9+a[i]);
}
double ans=-1e100,sum=0,pw=1;
for(int i=1;i<=n;i++)
{
sum+=pw,pw*=0.9;
ans=max(ans,dp[i]/sum-1200/sqrt(i));
}
Fix(20)<<ans;
}
F
扫描线,假设第一维扫描到 \(x\),则第一维覆盖的范围是 \([x,x+D)\)。下面考虑第二维。
那么就是维护 \(f(y)=[y,y+W)\) 的区间内总点数,(\(f(y)\))的最大值。
注意到扫描线移动一格时,其实就是加了一些点,删了一些点。
注意到加入点 \(a\) 时要将 \((a-W,a]\) 内点的 \(f\) 值加一,删除时就是减一。
区间加整体 \(\max\),一个线段树搞定。时间复杂度 \(\Theta(n\log n)\)。
const int N=200005,S=N-5;
int n,X,Y,x[N],y[N];
VI pos[N];
struct tree
{
struct node
{
int lz,mx;
}tr[N<<2];
#define mid (l+r>>1)
#define rmd (mid+1)
#define lid (id<<1)
#define rid (lid|1)
void pushup(int id)
{
tr[id].mx=max(tr[lid].mx,tr[rid].mx);
}
void pushdown(int id)
{
int &l=tr[id].lz;
tr[lid].lz+=l,tr[rid].lz+=l,tr[lid].mx+=l,tr[rid].mx+=l;
l=0;
}
void build(int l=1,int r=S-Y+1,int id=1)//size = S - Y + 1
{
tr[id].lz=tr[id].mx=0;
if(l==r)return;
build(l,mid,lid);
build(rmd,r,rid);
}
void modify(int ql,int qr,int v,int l=1,int r=S-Y+1,int id=1)
{
if(ql<=l&&qr>=r){tr[id].mx+=v,tr[id].lz+=v;return;}
pushdown(id);
if(ql<=mid)modify(ql,min(qr,mid),v,l,mid,lid);
if(qr>=rmd)modify(max(ql,rmd),qr,v,rmd,r,rid);
pushup(id);
}
int query(){return tr[1].mx;}
}t;
void Solve(int CASE)
{
cin>>n>>X>>Y;
for(int i=1;i<=n;i++)cin>>x[i]>>y[i],pos[x[i]].PB(y[i]);
t.build();
for(int i=1;i<X;i++)
for(int j:pos[i])t.modify(max(j-Y+1,1),min(j,S-Y+1),1);
int ans=0;
for(int i=1;i<=S-X+1;i++)
{
for(int j:pos[i+X-1])t.modify(max(j-Y+1,1),min(j,S-Y+1),1);
for(int j:pos[i-1])t.modify(max(j-Y+1,1),min(j,S-Y+1),-1);
ans=max(ans,t.query());
}
cout<<ans;
}
G
神仙容斥题。具体自己看题解。
题解式子有点问题。建议理解后自己推一遍,或者看这里的更正。
const int N=31,S=30;
MI C[N*N][N*N];
void Init()
{
C[0][0]=1;
for(int i=1;i<=S*S;i++)
for(int j=0;j<=i;j++)
C[i][j]=C[i-1][j]+(j?C[i-1][j-1]:0);
}
int n,m;
MI b(int x,int y)
{
MI ans=0;
for(int i=1;i<=y;i++)
ans+=MI((i^y)&1?-1:1)*C[y][i]*pw<MI>(i,x);
return ans;
}
MI f[N][N*N],g[N][N*N],h[N][N*N];
int E(int x){return (x>>1)*(x+1>>1);}
void Solve(int CASE)
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=0;j<=E(i);j++)
{
for(int k=0;k<=i;k++)
g[i][j]+=C[i][k]*C[k*(i-k)][j];
}
for(int i=1;i<=n;i++)
for(int j=0;j<=E(i);j++)
{
h[i][j]=g[i][j];
for(int k=1;k<i;k++)
for(int l=0;l<=j;l++)
h[i][j]-=C[i-1][k-1]*h[k][l]*g[i-k][j-l];
}
const int i2=499122177;
for(int i=1;i<=n;i++)
for(int j=0;j<=E(i);j++)
{
f[i][j]=h[i][j]*i2;
for(int k=1;k<i;k++)
for(int l=0;l<=j;l++)
f[i][j]+=C[i-1][k-1]*h[k][l]*f[i-k][j-l]*i2;
}
MI ans=0;
for(int i=0;i<=E(n);i++)
ans+=b(m,i)*f[n][i];
ans*=pw(2_M,m);
cout<<ans;
}