Codeforces Round #551 (Div. 2) 题解
A. Serval and Bus
有若干种公交车,第(i)种会从(s_i)时刻开始,每过(d_i)秒会出现一次。现在有一个人在(t_i)时刻到达车站,问它会碰到的第一辆车是哪一种。
傻逼题
#include<iostream>
#include<cstdio>
using namespace std;
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int ans=0,mxT=1e9,n,T;
int main()
{
n=read();T=read();
for(int i=1;i<=n;++i)
{
int x=read(),d=read();
while(x<T)x+=d;
if(mxT>x)mxT=x,ans=i;
}
cout<<ans<<endl;
return 0;
}
B. Serval and Toy Bricks
给你三视图,还原一个可能的图形。
俯视图告诉了哪些位置有东西。
左视图正视图告诉了每一行/每一列的最大值,
然后随手构造一下就行了。
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 120
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int h[MAX][MAX],a[MAX],b[MAX],n,m,H,c[MAX][MAX];
int main()
{
n=read();m=read();H=read();
for(int i=1;i<=m;++i)a[i]=read();
for(int i=1;i<=n;++i)b[i]=read();
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)c[i][j]=read();
for(int i=1;i<=m;++i)
for(int j=1;j<=n;++j)
{
if(!c[j][i])continue;
if(b[j]>=a[i])h[j][i]=max(h[j][i],a[i]);
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
if(!c[i][j])continue;
if(a[j]>=b[i])h[i][j]=max(h[i][j],b[i]);
}
for(int i=1;i<=n;++i,puts(""))
for(int j=1;j<=m;++j)printf("%d ",h[i][j]);
return 0;
}
C. Serval and Parenthesis Sequence
给你一个带有通配符的括号序列,你要构造一个合法的括号序列,使得除了本身外的每一个前缀都是不合法的括号序列。
仔细想想就会发现第一个位置一定是左括号,最后一个一定是右括号,且两个括号一定匹配。
问题变成了第(2)个位置到第(n-1)个位置必须是一个合法的括号序列。
那么算一下需要多少个左括号,前面全部填左括号,剩下的填右括号,再扫一遍判断是否合法即可。
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 300300
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
char s[MAX];int n;
void WA(){puts(":(");exit(0);}
void Output()
{
for(int i=1;i<=n;++i)putchar(s[i]);
puts("");exit(0);
}
int main()
{
n=read();scanf("%s",s+1);
if(n&1)WA();
if(s[1]==')')WA();
if(s[n]=='(')WA();
s[1]='(';s[n]=')';
if(n==2)Output();
int tt=0,q=0;
for(int i=2;i<n;++i)
if(s[i]=='(')tt+=1;
else if(s[i]==')')tt-=1;
else ++q;
if(q<abs(tt))WA();
int lf=(q-abs(tt))/2;if(tt<0)lf-=tt;
int cnt=0;
for(int i=2;i<n;++i)
if(s[i]=='?')
{
++cnt;
if(cnt<=lf)s[i]='(';
else s[i]=')';
}
for(int i=2,t=0;i<n;++i)
{
if(s[i]=='(')t+=1;
else t-=1;
if(t<0)WA();
}
Output();
return 0;
}
D. Serval and Rooted Tree
给你一棵树,每个点有一个(min)或者一个(max),表示其点权是所有儿子的的点权的最大值或者最小值。假设一共有(k)个叶子节点,那么每个叶子节点的点权是([1,k])中的一个数,并且每个叶子的点权必须不同。
求根节点的最大点权。
一个很简单的(dp)题,设(f[i])表示当前根节点的点权在子树的所有叶子的权值中最大排名第几,
如果这个点是(max),那么转移就是叶子个数减去某个子树(v)中的叶子个数加上(f[v])。
如果是(min),那么转移就是(1+sum f[v]-1)。
复杂度(O(n))。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define MAX 300300
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
vector<int> E[MAX];
int f[MAX],sz[MAX],a[MAX],n;
void dfs(int u)
{
if(!E[u].size()){f[u]=sz[u]=1;return;}
int mx=0;
for(int v:E[u])dfs(v),sz[u]+=sz[v];
for(int v:E[u])
if(a[u]==1)mx=max(mx,sz[u]-sz[v]+f[v]);
else mx+=f[v]-1;
if(a[u]==0)mx+=1;
f[u]=mx;
}
int main()
{
n=read();
for(int i=1;i<=n;++i)a[i]=read();
for(int i=2;i<=n;++i)E[read()].push_back(i);
dfs(1);printf("%d
",f[1]);
return 0;
}
E. Serval and Snake
交互题。
在(n*n)的网格中有一条每条边都平行于(x)轴或者(y)轴,且不交不成环的折线(就是一条贪吃蛇),你每次可以询问一个矩阵,交互库会回答这个矩形的边界和折线的交点数量。
你需要在(2n+log(n))次询问内找出这个折线的两个端点。
发现如果询问的矩形中包含了恰好一个端点,那么返回值就是奇数,否则是偶数。
那么先询问每一行和每一列,确定两个点在哪一行哪一列。
如果不在同一行或者同一列,额外询问一次就可以确定答案。
否则在同一行或者同一列,额外二分一下答案就可以了。
#include<iostream>
#include<cstdio>
using namespace std;
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n;
int Query(int x1,int y1,int x2,int y2)
{
printf("? %d %d %d %d
",x1,y1,x2,y2);
fflush(stdout);
return read();
}
void Answer(int x1,int y1,int x2,int y2)
{
printf("! %d %d %d %d
",x1,y1,x2,y2);
fflush(stdout);
}
int lx[20],t1,ly[20],t2;
int main()
{
n=read();
for(int i=1;i<=n;++i)if(Query(i,1,i,n)&1)lx[++t1]=i;
for(int i=1;i<=n;++i)if(Query(1,i,n,i)&1)ly[++t2]=i;
if(t1==2&&t2==2)
{
if(Query(lx[1],ly[1],lx[1],ly[1])&1)
Answer(lx[1],ly[1],lx[2],ly[2]);
else Answer(lx[1],ly[2],lx[2],ly[1]);
}
else if(t1==2)
{
int l=1,r=n,ret=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(Query(lx[1],1,lx[1],mid)&1)ret=mid,r=mid-1;
else l=mid+1;
}
Answer(lx[1],ret,lx[2],ret);
}
else
{
int l=1,r=n,ret=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(Query(1,ly[1],mid,ly[1])&1)ret=mid,r=mid-1;
else l=mid+1;
}
Answer(ret,ly[1],ret,ly[2]);
}
return 0;
}
F. Serval and Bonus Problem
在([0,l])的数轴上,随机(n)条线段(端点是实数),求被超过(k)条线段覆盖的区间的长度和的期望。
看ChineseRound的题解是真的舒服
首先既然是实数,那么长度是(l)和长度是(1)没有什么区别。
而合法区间的总长度和随机一个点(P),使得它在合法区间上的概率也是一样的。
那么(n)个区间一共有(2n)个端点,再加上(P)点,一共会产生(2n+1)个点,这些点也是可以随机产生的。我们现在要算的就是(P)点在合法区间上的概率。
于是现在的问题就是给你(2n+1)个点,怎么选择(P)点以及(n)条直线求(P)被至少(k)条覆盖的概率。
我们设(f[i][j][0/1])表示前(i)个点中,还有(j)个点没有找到匹配,是否已经选定了(P)点。
考虑转移:
- 这个点作为右端点:(f[i][j][k]*j ightarrow f[i+1][j-1][k])
- 这个点作为左端点:(f[i][j][k] ightarrow f[i+1][j+1][k])
- 这个点作为(P)点:(f[i][j][0] ightarrow f[i+1][j][1])
我们强制选择(P)点的时候(jge K),这样子算出来的就是至少被覆盖(K)次的方案数。
但是这样算出来的东西显然是算多的,因为我们这样子等价于强行把线段进行了。
考虑计算总方案,我们这样子来一种对应方法,即对于一个排列,前(2n)个点相邻的两个配对作为一条线段,最后一个点作为(P)点,但是这样子算重了,所以要除掉(n!*2^n)。
所以答案就是(displaystyle frac{f[2n+1][0][1]*L*n!*2^n}{(2n+1)!})。
#include<iostream>
#include<cstdio>
using namespace std;
#define MOD 998244353
#define MAX 4040
void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;}
int n,N,K,L,f[MAX][MAX][2],ans,inv[MAX];
int main()
{
cin>>n>>K>>L;N=n+n+1;
f[0][0][0]=1;
for(int i=1;i<=N;++i)
for(int j=0;j<i;++j)
{
add(f[i][j+1][0],f[i-1][j][0]);
add(f[i][j+1][1],f[i-1][j][1]);
if(j)add(f[i][j-1][0],1ll*f[i-1][j][0]*j%MOD);
if(j)add(f[i][j-1][1],1ll*f[i-1][j][1]*j%MOD);
if(j>=K)add(f[i][j][1],f[i-1][j][0]);
}
ans=1ll*L*f[N][0][1]%MOD;
for(int i=1;i<=n;++i)ans=2ll*ans*i%MOD;
inv[0]=inv[1]=1;for(int i=2;i<=N;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD,ans=1ll*ans*inv[i]%MOD;
printf("%d
",ans);
return 0;
}