CodeForces #545(div.2)题解

题目链接:Dashboard - Codeforces Round #545 (Div. 2) - Codeforces

A. Sushi for Two

解题思路:

利用"缩点"变成1,2,1,2...或者2,1,2,1...的模式然后每个点都有权值,权值就是原序列相同连续段的总数。最后枚举相邻的两个取max就好了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;
const int mx = 1e5 + 10;
struct node
{
	int a,k;
}s[mx];
int main(){
	int n,m = 1;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&s[i].a),s[i].k = 1;
	for(int i=2;i<=n;i++){
		if(s[i].a==s[m].a) s[m].k++;
		else s[++m] = s[i];
	}
	int ans = 0;
	for(int i=2;i<=m;i++) ans = max(ans,2*min(s[i-1].k,s[i].k));
	printf("%d\n",ans);
    return 0;
}

B. Circus

解题思路:

设(1,0)的有x人,(0,1)的有y人,那么当(y或者x)>n/2时,肯定是无解的。这个很好证明。

那么当x>=y时:

我们暂时把(1,0)都分配参加一次场表演,把(0,1)都分配给第二场,那么当前第一场愿意且参加的有x人,第二场有y人。

剩下的就是考虑(1,1)和(0,0)的情况了。设(1,1)有z个人,因为x<=y,所以有:

当z+x>=y时:

在z中取w个人,使得x+w==y,这样当前两场愿意且参加的人数暂时一样了,然后剩下z-w个(1,1),如果它是偶数那么就好办了,直接对分就可以了,最后再对分(0,0)就完事了。但如果他是奇数,我们就可以把剩一个(1,1)扔到第二场,把一个(0,1)换到第一场就好了。

当z+x<y时:

那么就让第一场愿意且参加得人是x+z,就是(1,0)和(1,1)都去第一场,那么第二场当然也留下x+z个人(0,1),剩下y - (x+z)个(9,1)都去第一场就好了,因为y<=n/2所以肯定是可以办到的。

同理当x>=y时,处理方法是一样的。只不过对象对调而已。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;
const int mx = 5e3 + 10;
int wa[mx],wb[mx],pa,pb;
int one[mx],fi[mx],zer[mx],se[mx];
char str[mx];
bool vis[mx];
int main(){
	int n,x = 0,y = 0,z = 0,w = 0;
	bool ok = 0;
	scanf("%d",&n);
	scanf("%s",str);
	for(int i=1;i<=n;i++) wa[i] = str[i-1] - '0';
	scanf("%s",str);
	for(int i=1;i<=n;i++) wb[i] = str[i-1] - '0';
	int *f = fi,*s = se;
	for(int i=1;i<=n;i++){
		if(wa[i]&&wb[i]){
			one[z++] = i;
		}else if(wa[i]){
			fi[x++] = i;
		}else if(wb[i]){
			se[y++] = i;
		}else zer[w++] = i;
	}
	if(x+y==0&&(z&1)) return 0*puts("-1");
	if(x+y==0){
		for(int i=0;i<z/2;i++) vis[one[i]] = 1;
		for(int i=0;i<n/2-z/2;i++) vis[zer[i]] = 1;
	}
	if(y>n/2||x>n/2) return 0*puts("-1");
	if(x>y){
		swap(x,y);
		swap(f,s);
		ok = 1;
	} 
	if(x+z>=y){
		int c = n/2 - x;
		for(int i=0;i<x;i++) vis[f[i]] = 1;
		int j = z - (y - x);
		if(j&1) vis[s[0]] = 1,c--;
		j = j/2 + (y-x),c -= j;
		for(int i=0;i<j;i++) vis[one[i]] = 1;
		for(int i=0;i<c;i++) vis[zer[i]] = 1;
	}else{
		for(int i=0;i<x;i++) vis[f[i]] = 1;
		for(int i=0;i<z;i++) vis[one[i]] = 1;
		int j = y - z - x,c = n/2 - x - z - j;
		for(int i=0;i<j;i++) vis[s[i]] = 1;
		for(int i=0;i<c;i++) vis[zer[i]] = 1;
	}
	for(int i=1;i<=n;i++) if(ok^vis[i]) printf("%d ",i);
    return 0*puts("");
}

C. Skyscrapers

解题思路:

这题简单直接,排序去重然后二分找排名,a[i][j]在两个数组中的排名小的那个补到跟另一个大的一样大就好了,最后就是去小的排名中最大排名加上排名差和大的那个最大排名取MAX就好了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;
const int mx = 1e3 + 10;
int a[mx][mx];
int r[mx][mx],c[mx][mx];
int lr[mx],lc[mx]; 
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++)
	for(int j=0;j<m;j++) scanf("%d",a[i]+j);
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++) r[i][j] = a[i][j];
		sort(r[i],r[i]+m);
		lr[i] = unique(r[i],r[i]+m) - r[i];
	}
	for(int j=0;j<m;j++){
		for(int i=0;i<n;i++) c[j][i] = a[i][j];
		sort(c[j],c[j]+n);
		lc[j] = unique(c[j],c[j]+n) - c[j];
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			int v = lower_bound(r[i],r[i]+lr[i],a[i][j]) - r[i];
			int u = lower_bound(c[j],c[j]+lc[j],a[i][j]) - c[j];
			if(v<u) v = lr[i] + u - v,u = lc[j];
			else u = lc[j] + v - u,v = lr[i];
			printf("%d ",max(u,v)); 
		}
		puts("");
	}
    return 0;
}

D. Camp Schedule

解题思路:

用KMP解决t串的后缀和前缀的最大公共长度,之后就一直复制下去就好了。。。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#include<cstring> 
using namespace std;
typedef long long ll;
const int mx = 5e5 + 10;
char str[mx],Ts[mx];
int one,zer,nxt[mx];
void kmp(char *p)
{
	int i = -1,j = 0;
	nxt[0] = -1;
	while(p[j]){
		if(i==-1||p[i]==p[j])
		{
			nxt[++j] = ++i;
		}else i = nxt[i];
	}
}
int main(){
	scanf("%s",str);
	for(int i=0;str[i];i++) 
	if(str[i]=='1') one++; else zer++;
	scanf("%s",Ts);
	int len = strlen(Ts),a = 0,b = 0;
	for(int i=0;Ts[i];i++)
	if(Ts[i]=='1') a++;	else b++; 
	kmp(Ts);
	if(one<a||zer<b) return 0*puts(str);
	one -= a,zer -=b,a = b = 0;
	for(int i=nxt[len];Ts[i];i++)
	if(Ts[i]=='1') a++; else b++;
	printf("%s",Ts);
	while(one>=a&&zer>=b)
	{
		printf("%s",Ts+nxt[len]);
		one -= a,zer -= b;
	}
	for(int i=0;i<one;i++) putchar('1');
	for(int i=0;i<zer;i++) putchar('0');
    return 0*puts("");
}

E. Museums Tour

解题思路:

可想而知一个点一共会有d种状态,那么我们何不把这些状态都拆分出来,这样既变成了n*d个点,m*d条边。

然后tarjan缩点之后跑dp就可以了。但是5e6的点会递归爆栈,那么我们就把他弄成inline内联函数,节省一下栈内存。

#include <bits/stdc++.h>
using namespace std;
const int mx = 5e6 + 10;
int n,m,d;
int hd1[mx],tot1,hd2[mx],tot2;
struct node
{
	int v,nxt;
}e1[mx],e2[mx];
char str[100010][55];
int dfn[mx],id[mx],is,siz,q[mx];
int ty[mx],type,cnt[mx],in[mx];
bool vis[mx];
void add1(int u,int v)
{
	e1[tot1] = {v,hd1[u]};
	hd1[u] = tot1++;	
}
void add2(int u,int v)
{
	e2[tot2] = {v,hd2[u]};
	hd2[u] = tot2++;	
}
inline void tarjan(int u)
{
	dfn[u] = id[u] = ++is;
	vis[u] = 1;q[++siz] = u;
	for(int i=hd1[u];~i;i=e1[i].nxt)
	{
		int v = e1[i].v;
		if(!dfn[v]){
			tarjan(v);
			id[u] = min(id[v],id[u]);
		}else if(vis[v])
		id[u] = min(id[u],dfn[v]);
	} 
	if(dfn[u]==id[u]){
		type++;
		do{
			ty[q[siz]] = type;
			int r = q[siz] / d, c = q[siz] % d;
			if(in[r]!=type&&str[r][c]=='1') 
			cnt[type]++,in[r] = type;
			vis[q[siz]] = 0; 
		}while(q[siz--]!=u);
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&d);
	int u,v;
	memset(hd1,-1,sizeof(hd1));
	memset(hd2,-1,sizeof(hd2)); 
	for(int i=0;i<m;i++){
		scanf("%d%d",&u,&v);
		for(int j=0;j<d;j++)
		add1((u-1)*d+j,(v-1)*d+(j+1)%d); 
	}
	for(int i=0;i<n;i++) scanf("%s",str[i]);
	tarjan(0);
	for(int i=0;i<n*d;i++){
		if(ty[i])
		for(int j=hd1[i];~j;j=e1[j].nxt){
			v = e1[j].v;
			if(ty[i]!=ty[v]) add2(ty[i],ty[v]);	
		}
	}
	int ans = 0;
	for(int i=1;i<=type;i++){//拓扑排序 
		int ma = 0;
		for(int j=hd2[i];~j;j=e2[j].nxt)
		ma = max(ma,cnt[e2[j].v]);
		cnt[i] += ma;
	}
	printf("%d\n",cnt[type]);
	return 0;
}

F. Cooperative Game

解题思路:

(交互机模式还是第一次做。。。)

交互机会告诉你你每次移动之后的结果,哪几个点是在一起的,但它最多只跟你说3(t+c)次。

而我们也不知道t和c。所以就算10个点最后再一次了,我们还要去判断是不是在终点集合。

所以只能试着想用解方程来解决这个问题。并且尽量把移动弄的简单一点。(真的可以在三种移动情况下解决它)

1.next 0 1  2.next 0  3.next 0 1 2 3 4 5 6 7 8 9

当当讨论前两种,发现0走的步数是1的两倍,假设他们会在c的某个点相遇,又s为1走的步数。那么有s = t + y*c + a(a<c)

又设w为0走的步数,w = t + x*c + a(a<c)。那么有2*s ==w ,2*t + 2*y*c + 2*a == t + x*c + a。

t = (x-2*y)*c - a。w + t = t + x*c + a + (x-2*y)*c - a = t + 2*(x-y)*c。

依次可得当0走了w步,1走了s步两点在c中相遇时,再走t步就可以达了终点,那么我们另剩下的2,3,4,5,6,7,8,9一起从开始点开始走,他们就一起到达了终点。

步数证明:

所以他们最后走了t + 2*(x-y)*c步。

当t<=c时,所以答案会是t + 2*c。(x-y肯定为1)(证明简单略过,自己证明吧)

当t>c时,当1第一次到达终点时,0走了2*t步,此时0最多走两圈和1相遇(也就是同在终点时),就是2*c,最后再走t步全部到达终点。所以总步数为3*t+2*c。

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <cstring> 
#include <vector>
#define fi first
#define se second
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair <int,int> pa;
const int mx = 1e5 + 10;
const int mod = 1e9 + 7;
int main()
{
	int val;
	do{
		puts("next 0 1");fflush(stdout);
		scanf("%d %*[^\n]",&val);
		puts("next 0");fflush(stdout);
		scanf("%d %*[^\n]",&val);
	}while(val==3);
	do{
		puts("next 0 1 2 3 4 5 6 7 8 9");fflush(stdout);
		scanf("%d %*[^\n]",&val);
	}while(val==2);
	puts("done");fflush(stdout);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值