zoukankan      html  css  js  c++  java
  • Codeforces Round #551 (Div. 2) 题解

    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;
    }
    
  • 相关阅读:
    获得随机N位数不重复数字
    springboot配置swagger
    Java开发中的23种设计模式详解(转)
    英文对应解释
    python实现ping命令语音播报联通性V1.0.0.1
    python实现ping命令语音播报联通性V1.0
    python实现ping命令语音播报联通性
    eclipse更新支持tomcat-8.0以上
    网管---------Windows杀死后台进程查询等脚本
    网管-windows磁盘检查修复
  • 原文地址:https://www.cnblogs.com/cjyyb/p/10706846.html
Copyright © 2011-2022 走看看