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\n",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\n",x1,y1,x2,y2);
	fflush(stdout);
	return read();
}
void Answer(int x1,int y1,int x2,int y2)
{
	printf("! %d %d %d %d\n",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\rightarrow f[i+1][j-1][k]\)
  • 這個點作為左端點:\(f[i][j][k]\rightarrow f[i+1][j+1][k]\)
  • 這個點作為\(P\)點:\(f[i][j][0]\rightarrow f[i+1][j][1]\)

我們強制選擇\(P\)點的時候\(j\ge 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\n",ans);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM