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

链接:http://codeforces.com/contest/1025

远离pupil,从我做起。

目录

A.Doggo Recoloring

题目

题解

代码

B. Weakened Common Divisor

题目

题解

C.Plasticine zebra

题目

题解

代码


A.Doggo Recoloring

题目

          给定一个字符串,字符串任意一个出现>=2次的字符都可以集体被替换成另一个,比如“abababc”可以变成"zbzbzbc", "bbbbbbc", "aaaaaac", "acacacc",但不能变换最后一个c,因为它只出现了一次。给一个字符串,问能否通过若干次上述变换,使得字符串里每个字符都一样。

题解

            至少有一个出现次数>=2的字符即可。当然字符串长度为1的情况要特殊考虑一下。

代码

#include <bits/stdc++.h>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8

typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 200005;

inline void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

inline void print(int x)
{
    if(x<0){ putchar('-'); x=-x;}
    if(x>9) print(x/10);
    putchar(x%10+'0');
}

inline void caltime(int tt) {
	tt = clock() - tt;
    cerr << (double)tt/CLOCKS_PER_SEC << " seconds!" << endl;
}

int n;
string s;
int a[100];

int main()
{
	cin>>n>>s;
	for(int i=0;i<n;i++) {
		int temp = s[i]-'a';
		a[temp]++;
	}
	int ans = 0;
	for(int i=0;i<26;i++) {
		if(a[i]>=2) {
			ans++;
		}
	}
	if(ans||n==1) {
		cout<<"Yes"<<endl;
	}else {
		cout<<"No"<<endl;
	}
	return 0;
}

B. Weakened Common Divisor

题目

          给n组数据,每组有一个a和b,要求一个公约数x,使得每组的a和b至少有一个含有这个约数x。如果x为1的话输出-1,否则输出x。(x可能有多个,随便输出一个就行了)

题解

          什么神题这都是。。。

          这题最难处理的地方在于,每组有两个数,只要任意一个的公约数满足就行了。于是我写了个DFS,果然TLE了- -

(看一下代码吧)

#include <bits/stdc++.h>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8

typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 150005;

inline void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

int gcd(int a,int b) {
	if(b==0)	return a;
	return gcd(b,a%b);
}

int n;
int a[maxn],b[maxn];
int tot1,tot2,tot;

void dfs(int x,int ans) {
	if(x==n-1) {
		tot = ans;
		return;
	}
	for(int i=0;i<2;i++) {
		if(i&1) {
			int temp = gcd(a[x+1],ans);
			if(temp==1) {
				return;
			}else {
				dfs(x+1,temp);
			}
		}else {
			int temp = gcd(b[x+1],ans);
			if(temp==1) {
				return;
			}else {
				dfs(x+1,temp);
			}
		}
	}	
}

int main()
{
	cin>>n;
	for(int i=0;i<n;i++) {
		read(a[i]);
		read(b[i]);
	}
	dfs(0,a[0]);
	tot1 = tot;
	if(tot<=0) {
		tot = 0;
		dfs(0,b[0]);
	}
	if(tot<=0) {
		cout<<-1<<endl;
	} else{
		cout<<tot<<endl;
	}
	return 0;
}

          每组a和b的因数不同,但是如果把a和b的因数全都放到一块,问题就解决啦。

          于是,我们要求每组a和b的最小公倍数!!!然后再用一个x,与所有组的最小公倍数求最大公约数。

           这样之后,x含有的因数必然是所有组的因数之一。 但是x本身可能会超出范围,因为它是若干组因数的乘积。于是就有了解法1.

#include <bits/stdc++.h>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8

typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 150005;

ll gcd(ll a,ll b) {
	if(b==0)	return a;
	return gcd(b,a%b);
}

ll n;
ll a,b;
ll tot1,tot2,tot;

int main()
{
	cin>>n;
	ll x = 0;
	for(int i=0;i<n;i++) {
		scanf("%lld%lld",&a,&b);
		a = a/gcd(a,b)*b;
		x = gcd(x,a);
	}
	if(x==1) {
		cout<<-1<<endl;
		return 0;
	}
	for(int i=2;i*i<=x;i++) {
		//cout<<i<<endl;
		if(x%i==0)	{
			cout<<i<<endl;
			return 0;
		}
	}
	cout<<x; //如果x没有大于1的因数,说明它是个素数,但由于它肯定符合条件,直接输出x就行了
	return 0;
}

这时候,遇到了神仙数据

2
1999999973 1999999943
1999999973 1999999943

这个数求出来的x是1e18的,开了根号也是1e9,会TLE,只能改进做法了。

我们用x重新与每组的a和b都去求最大公约数,并取其较大值。因为在x!=1的情况下,说明肯定存在解,而且之前说过了,x的所有>1的因数必然是正解,那么取较大的解肯定没错。(也可以不取较大解,只要取公约数>1的解就行了。)

for(int i=0;i<n;i++) {
	if(gcd(x,a[i])>1) {
		x = gcd(x,a[i]);
	}
	if(gcd(x,b[i])>1) {
		x = gcd(x,b[i]);
	}
}
for(int i=0;i<n;i++) {
	x = max(gcd(x,a[i]),gcd(x,b[i]));
}

以上的两种写法都是对的。


C.Plasticine zebra

题目

            给一个字符串,只由b和w组成,现在可以进行若干次操作,每次操作可以选择一个位置,使这个位置左右两边的字符串都倒过来。

举个栗子,”bw | bbw“ 变成 "wb | wbb"。一个字符串的最大价值记为该字符串中最长的交替串(即"bwbwbw"这类的),问怎么样才能使价值最大,求最大价值。

题解

           我们用数组c来记录,如果一个字符和其后面一个字符相等,那么值为0,否则值为1,最后一个字符看成一个环,它的下一个字符就是第一个字符。

            由于翻转操作,如果字符串wbwbbbwb,那么对于数组c的值是11100111,我们可以把开头3个1和结尾3个1连起来,方法就是以开头第(3+1)个位置为分界旋转。

            无论怎么变换,结尾和开头的元素经过翻转后必然会碰到一块。同理,相邻的两个元素,一次翻转后会变成开头结尾,再次翻转又会相邻。

            如果c数组为0110110(对应wwbwwbw) ,那么这两块1是无论如何也拼不到一起的。

            原问题便转化成了,求一个环中连续1的最大长度。

代码

#include <bits/stdc++.h>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8

typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 200005;

inline void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

inline void print(int x)
{
    if(x<0){ putchar('-'); x=-x;}
    if(x>9) print(x/10);
    putchar(x%10+'0');
}

inline void caltime(int tt) {
	tt = clock() - tt;
    cerr << (double)tt/CLOCKS_PER_SEC << " seconds!" << endl;
}

string s;
int c[maxn];

int main()
{
	cin>>s;
	int n = s.length();
	for(int i=0;i<n;i++) {
		c[i] = (s[i]!=s[(i+1)%n]);
	}
	int s = 1,ans = 1;
	for(int i=0;i<2*n;i++) {
		if(c[i%n]==0) s = 1;	
		else {
			s++;
			ans = max(ans,s);
		}
	}
	if(ans>=n)
		ans = n;
	cout<<ans<<endl;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

总想玩世不恭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值