Codeforces Round #505 (rated, Div. 1 + Div. 2, based on VK Cup 2018 Final)

A. Doggo Recoloring

http://codeforces.com/contest/1025/problem/A

题目大意为将一个n长的字母串全部染成同一个颜色(字母),但每次染色至少要有选择两个相同的字母。
可以染色任意次,问给定一个字符串,是否最后可以全部染成同一种颜色。

一开始错了1次因为题目意思都没看清,然后又因为没有考虑到只有一个字母的情况错了一次。

最后代码

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100000+10;

char s[MAXN];
int n;
int t[26];
int main() {
    cin>>n;
    cin>>s;
    for(int i=0;i<n;i++)
        t[s[i]-'a']++;
    sort(t,t+26);

    if(n!=1&&t[25]==1){
        puts("No");
    }else
        puts("Yes");
    return 0;
}

其实可以不用排序,也就是只统计是否出现过同种颜色(字母)两次以上,如果出现,则一定可以将所有字母染成同一种颜色(字母),
另外注意只有一个字母的时候直接成立。

#include<bits/stdc++.h>
using namespace std;
int n,i,r,a[256];
char x;
int main(){
	for(cin>>n;i<n;i++)cin>>x,(a[x]>0?r=1:0),a[x]++;
	cout<<(r==1||n==1?"Yes":"No");
}

B. Weakened Common Divisor
http://codeforces.com/contest/1025/problem/B
大意:求出weakened common divisor (WCD)
给出n对数,他们的WCD为可以将每一对数中至少一个数整除的数(1除外),如果不存在则输出-1.存在多个则输出任意一个。

一开始直接想把每对数乘起来变成n个数,然后求n个数的最大公约数(GCD),发现逻辑上不对

Examples
input
3
17 18
15 24
12 15
output
6
相乘得到的数分别是306 360 180,他们的最大公约数可以是18,但明显18不是15的因子也不是24的因子,同样18也不是(12,15)的因子。主要是因为相乘因子变大了。
后面又试了用set先存储第一对数的所有因子,然后用后面的数进行排除错误因子,结果最后超时了。
最后找到的ac代码
代码一:将第一对数保留(a,b),后面每一对数相乘,变成一个数x。然后更新(a,b),将其替换成gcd(a,x),gcd(b,x)。最后再输出a或者b的最小因子(避免了我的情况一)。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a,b,n;
ll gcd(ll x,ll y){return x%y? gcd(y,x%y):y;}
int main(){
	cin>>n;
	cin>>a>>b;
	for(int i=1;i<n;++i){
		ll x,y;cin>>x>>y;
		x*=y;a=gcd(a,x);b=gcd(b,x);
	}
	if(a==1&&b==1){cout<<"-1\n";return 0;}
	for(ll i=2;i*i<=a||i*i<=b;++i)
		if(a%i==0||b%i==0){cout<<i<<endl;return 0;}
	if(a!=1)cout<<a<<endl;
	else cout<<b<<endl;
	return 0;
}

代码二:统计第一对数字的所有因子,然后对后面的每一对数进行测试,如果后面每一对数中只是有一个可以被因子可以整除,则输出因子。对应的我的情况二,没有使用set而是使用了p【c++】这种统计方法
对于第一对数的所有因子直接测试到有一个合理的因子就退出,节省了时间。

#include<cstdio>
int n,a[150010],b[150010],p[100],c;
void d(int x){
	for(int i=2;1ll*i*i<=x;i++)if(x%i==0){
		p[c++]=i;
		while(x%i==0)x/=i;
	}
	if(x>1)p[c++]=x;
}
int main(){
	scanf("%d",&n);
	for(int i=0;i<n;i++)scanf("%d%d",a+i,b+i);
	d(a[0]);
	d(b[0]);
	for(int i=0;i<c;i++){
		bool fl=1;
		for(int j=0;j<n&&fl;j++)if(a[j]%p[i]!=0&&b[j]%p[i]!=0)fl=0;
		if(fl){
			printf("%d\n",p[i]);
			return 0;
		}
	}
	puts("-1");
}

C. Plasticine zebra
http://codeforces.com/contest/1025/problem/C
题目大意:输出一个字符串中最长“斑马条纹”的长度,“斑马条纹”指类似于“wbwbwbw”(7)、“bwbwb”(5)。
同时为了最终获得更长的长度,可以在组装斑马之前,Grisha可以进行以下操作0或更多次。他将序列在某个地方分成两部分,然后将它们中的每一部分反转并再次将它们粘在一起。例如,如果Grisha的顺序为“ bwbbw ”(这里' b '表示黑条,' w '表示白条),那么他可以将序列拆分为bw | bbw(此处垂直条代表切割),反转两个部分并获得“ wbwbb ”。

例如
input:
bwwwbwwbw
output:
5

备注:在第一个例子中,可能的操作顺序之一是bwwbww | bw → w | wbwwwbwb → wbwbw wwbw,给出的答案等于5。

思路:
从样例一中逆推,发现最后得到的斑马条纹其实始终来自序列的两侧,经过操作后拼接在一起,于是我们可以直接将两个s拼接在一起,创造出一个2s,此时找出2s中的最长斑马条纹即可。
但要注意最长长度不会超过s的长度,第一次就错在这里。

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100000*2+10;
typedef long long ll;

string s;
ll t[MAXN];

int main() {
    cin>>s;
    s=s+s;
    ll len=s.length();
    ll ans=0;
    for(int i=1;i<len;i++){
        if(s[i]!=s[i-1]){
            t[i]=t[i-1]+1;
            ans=max(ans,t[i]);
        }
    }
    printf("%ld\n",min(ans+1,len/2));
    return 0;
}

D. Recovering BST
http://codeforces.com/contest/1025/problem/D
题目大意是给你一个n长的升序序列,问你是否可以将其构建成一个二叉树,并且二叉树的直接边连接的任何两个顶点的最大公约数值都超过1。

一开始只想到先将所有的有公共因子的数记录下,在染色,如果是可以将整个序列染成同样的颜色就感觉可以构成题意的二叉树,实际上是错的,当一个数作为根时,它左边的数(比他小)和右边的数(比他大)即便有公共因子也不能直接建立联系了。

看了一个ac的代码用dp的方法来解决,也比较简洁,具体解释看代码注释。注释是我按照自己理解写的,可能存在问题(毕竟我没过)。

#include<bits/stdc++.h>
int n,s[704],g[705][705],L[705][705],R[705][705],f[705][705];
int main() {
	for(int i=0;i<=(n=s[0]);i++)
        scanf("%d",&s[i]);

    //g[i][j]记录i,j是否存在公共因子。如果有则为1,否则为0。
	for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            g[i][j]=std::__gcd(s[i],s[j])>1;

    /*L[i][j]表示i到j可以作为左子树,R[i][j]表示i到j可以作为右子树*/
	for(int i=1;i<=n;i++)
        L[i][i]=R[i][i]=1;

    /*f[i][j]表示i到j中间存在合理的二叉树*/
    //注意遍历的过程,从小范围的序列长度遍历到大范围的序列长度
	for(int i=n;i>=1;i--)
        for(int j=i;j<=n;j++)
            for(int k=i;k<=j;k++)//k为这个二叉树的根
                if(L[i][k]&&R[k][j])
                    f[i][j]|=1,
                    R[i-1][j]|=g[k][i-1],//如果k和i-1有公共因子,则i-1到k可以作为右子树
                    L[i][j+1]|=g[k][j+1];//如果k和j+1有公共因子,则k到j+1可以作为左子树

    //f[1][n]若成立则整个序列可以转换成一个合题意的二叉树
	return puts(f[1][n]?"Yes":"No"),0;
}

EFG题因为没打,就不更新了。

posted @ 2018-08-20 09:10  零原创  阅读(256)  评论(0编辑  收藏  举报