Codeforces Round #551 (Div. 2)

题目地址

出奇的差。需要好好训练了。

但是最近课程任务比较重,需要好好抓紧。PS:暂时刷刷小cf安慰下自己,明天刷套div3准备后天div3。

只有A~D的题解:E题可能会补,交互题所以随缘补。如果明天有空。明天如果不补的话可能就会鸽了。

A. Serval and Bus

好久没有做CF,A题使我自闭。

告诉多次班车的初始时间、每隔多少分钟来一次。

告诉小A到达时间,距离小A最近的是第几班车。

题解:等差数列求解刚好比小A大的班车到达时间,需要注意初始小A到达时间可能比你首班车要短。

#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;

const int maxv=100000;
const int maxm=100000;

int n,t;
int ans,val=0x3f3f3f3f;

int main(){
	cin>>n>>t;
	FOR(i,1,n){
		int x,y;
		scanf("%d%d",&x,&y);
		int n;
		if(t>x)n=(t-x+y-1)/y+1;
		else n=1;
		if(x+(n-1)*y-t<val){
			val=x+(n-1)*y-t;
			ans=i;
		}
	}
	cout<<ans<<endl;
}

B - Serval and Toy Bricks

告诉你三视图,给出可能情况(任意)。

题解:一开始不知道任意,懵逼了好久。

俯视图给出只有哪个点有。

左视图告诉你每行最大,前视图告诉你每列最大。

所以用俯视图判断填哪个,对于具体哪个,判断该行最大和该列最大哪个比较小,用小的填肯定不冲突。

当然不可能出现都较小,导致矛盾,如果每列最大都是3,第一行最大是4,肯定这个图画不出来。所以如果最后判断有矛盾,

局部矛盾解决了,整体还有矛盾,就是画不了图。当然题目保证一定可以..所以不需要判断。

显然复杂度O(n2)。就算非要加判断也是O(n2)

#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;

int M[105][105][105];

int n,m,h;

int mb[105],ml[105];
int flag[105][105];
int Map[104][105];

int main(){
	cin>>n>>m>>h;
	FOR(i,1,m)scanf("%d",&mb[i]);
	FOR(i,1,n)scanf("%d",&ml[i]);
	FOR(i,1,n)FOR(j,1,m)scanf("%d",&flag[i][j]);
	FOR(i,1,n)FOR(j,1,m)if(flag[i][j]){
		if(mb[j]<=ml[i])Map[i][j]=mb[j];
		else Map[i][j]=ml[i];
	}	
	FOR(i,1,n){
		FOR(j,1,m){
			printf("%d ",Map[i][j]);
		}
		printf("\n");
	}
} 

C - Serval and Parenthesis Sequence

在给出的括号序列中?替换成括号,使得最后的序列前缀非法,整体合法。

题解:不合法只有可能对于前缀,左右括号不等,要保证整体合法,就要保证前缀中不能是右括号多非法,因为这种情况会导致整体非法,但左括号多非法不会导致整体非法。

把一半的左括号先填完。

当然这样肯定是最优的:如果不合法,就是已有右括号过多,左括号放满也不合法,这样会导致影响到整体的右括号多非法。

所以填完还要继续判断。

#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
	
int n;
string s;
int st[500005];
int R;
	
bool check(string s){
		FOR(i,1,n){
		if(s[i-1]=='(')st[++R]=1;
		else R--;
		//cout<<st.size()<<endl;
		if(R<0)return false;
		if(i!=n&&R==0)return false;
		if(i==n&&R!=0)return false;
	}
	return true;
}
	
int main(){
	cin>>n;
	cin>>s;
	int l=0,r=0;
	FOR(i,1,n)if(s[i-1]=='(')l++;
	if(n%2){
		puts(":(");return 0;
	}
	FOR(i,1,n){
		if(s[i-1]=='?'){
			if(l<n/2){
				l++;s[i-1]='(';
			}else{
				r++;s[i-1]=')';
			}
		}
	}
	if(!check(s))puts(":(");
	else cout<<s<<endl;
}

D - Serval and Rooted Tree

每个节点的number等于:max:孩子最大,min:孩子最小

叶子结点的max、min无所谓,通过分配1~k给叶子结点,使根节点1最大。

题解在注释中。

#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;

const int maxn = 300050;
int n,k;
int flag[maxn];
int dp[maxn]; 
vector<int>g[maxn];

void dfs(int u,int f){
	if(g[u].size()==0){
		dp[u]=1;k++;
		/******
		考虑对于这颗树、实现树的最优、自然能实现子树的最优:
		子树根节点最大,自然树的根节点会最大。
		把答案转换为表示第几大的数,因为数只可能出现在叶子结点中。
		那么对于根节点和孩子结点,孩子结点实现最优的情况下,
		根节点min: 将最优排名相加,就是根节点最优排名,不可能再好了。 
		max:取最小最优排名,只要能够实现这个最优,其他最优能否实现都无所谓。 
		dp[x]表示点x能取到的第dp[x]大的叶子结点:最后迭代出1取到的dp[1]大的叶子 
		为了实现最优,对于每个子树,我们都考虑前几大的叶子都在这个子树里,最后把效果叠加。
		比如1->2、1->3、3->4、3->5、4->6、4->7
		2:max,3:min
		对于2假设都是前几大的数,(这里其实只要最大在就可以了) ,则dp[2]=min(dp[child])=1
		对于3假设都是前几大的数,dp[3]+=dp[child](使min最大),dp[3]=2;
		到1结点,这时候2、3需要将效果叠加:
		如果1:max,取出最高排名,自然是1,因为只要在2结点实现最优情况即可(取孩子结点最大值,即最优实现)
		如果1:min,首先孩子结点已经是最优结果,排名叠加即可,dp[1]=dp[2]+dp[3]=3;第3大:2为答案。
		叠加也就是说:对于1个孩子结点实现最优的情况下,在这基础上对另一个孩子实现最优,dp[2]=1,第一名肯定在这里,那么
		从第一名后的第二、第三都在我这里。
		min把自己的孩子结点尽量设的名次够高,让所有结点都得实现最优。max只要一个孩子结点够高所以只取min(dp[child])让最小的结点实现最优其他无所谓。 
		*******/ 
		return;
	}
	if(flag[u]){
		dp[u]=0x3f3f3f3f;
		for(auto v:g[u])if(v!=f){
			dfs(v,u);
			dp[u]=min(dp[u],dp[v]);
		}
	}
	else{
		dp[u]=0;
		for(auto v:g[u])if(v!=u){
			dfs(v,u);
			dp[u]+=dp[v];
		}
	}
} 

int main(){
	cin>>n;
	FOR(i,1,n)scanf("%d",&flag[i]);
	FOR(i,2,n){
		int x;
		scanf("%d",&x);	
		g[x].push_back(i);
	}
	dfs(1,-1);
	cout<<k-dp[1]+1<<endl;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值