AIM Tech Round Div 1

AIM Tech Round Div 1

春节过完急急忙忙先来水几道题...

    A:题意:给定由一个字符串凿出一个图的过程:字符串仅有abc三种字符,造出的图中第i个顶点表示原来的第i个字符,ij有连边当且仅当s[i]s[j]相同或者s[i]s[j]是相邻的字符((ab),(bc)),(ac)不算。现在给出由某个字符串造出的图,构造一个符合要求的字符串。

    我码了90行显然有问题....

    我的做法是先看看是不是由一种字符组成的(这种情况可以包含仅有两种相邻字符的情况),这种情况直接看是不是完全图就可以了。

    不是上面那种情况的话我们观察到b字符一定和所有点有连边,那么b就全被找出来了,我们在找到两个没连边的点,那么他们一定分别是ac,再把与他们相连却又不是b的点标成自己的字符。

    (我天真的认为这就完了,但是还有许多情况没有考虑,注意到有无解的情况,所以我们要对划出来的ac扫一遍,所有的a必须构成团,c同理,并且ac之间不能有任何连边) 。

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
int n,m,a,b,A,B,num,c[510];
char mark[510];
bool list[510][510];
bool check() {
	for(int i = 1;i <= n;i ++)
		for(int j = i + 1;j <= n;j ++)
		{
			if(mark[i] == mark[j] && !list[i][j]) 
			{
				return false;
			}
			if(abs(mark[j] - mark[i] == 1) && !list[i][j])
			{
				return false;
			}
			if(abs(mark[j] - mark[i] == 2) && list[i][j])
			{
				return false;
			}
		}
	return true;
}
int main() {
	scanf("%d%d",&n,&m);
	if(m == (n * (n - 1)) / 2)
	{
		printf("Yes\n");
		for(int i = 1;i <= n;i ++) printf("a");
		return 0;
	}
	for(int i = 1;i <= m;i ++)
	{
		scanf("%d%d",&a,&b);
		list[a][b] = list[b][a] = true;
		c[a] ++;
		c[b] ++;
	}
	for(int i = 1;i <= n;i ++)
		if(c[i] == n - 1)
		{
			mark[i] = 'b';
			num ++;
		}
	for(int i = 1;i <= n;i ++)
	{
		bool F = false;
 		for(int j = i + 1;j <= n;j ++)
		{
 			if(list[i][j] == false)
			{
				mark[i] = 'a';
				mark[j] = 'c';
				num += 2;
				A = i;
				B = j;
				F = true;
				break;
			}
		}
		if(F) break;
	}
	for(int i = 1;i <= n;i ++)
		if(list[A][i] && mark[i] != 'b')
		{
			mark[i] = 'a';
			num ++;
		}
	for(int i = 1;i <= n;i ++)
		if(list[B][i] && mark[i] != 'b')
		{
			mark[i]  = 'c';
			num++;
		}
	if(num != n || ! check()) printf("No");
	else 
	{
		printf("Yes\n");
		for(int i = 1;i <= n;i ++) printf("%c",mark[i]);
	}
	return 0;
}     


    B:题意:给出一个序列,你需要使用以下两种操作使得序列的所有数的gcd不为1.

    1:删除一段连续的子序列(不能删完),代价为a*删除的长度。

    2:把一个点上的数修改最大1+1或者-1),每修改一个数代价为b,每个数只能修改1次。ab给出)。

    想了好久好久,首先可以看出是一个Dp,但是在最终gcd不确定的情况下仿佛很难Dp,而最终gcd仿佛很多。

    问题就在于这个最终gcd上,首先,因为题目中不能把序列删完,所以说头或者尾一定是留下的!其次因为题目要求仅仅是gcd不为1,那么其实最后gcd是多少都行(只要不是1) ,所以我们只用考虑A[1]-1,A[1],A[1]+1,A[n],A[n]-1,A[n]+1的质因子就行了!

    然后就简单了,我们设Dp[i][0/1/2]表示到了第i个数,子序列还没删,正在删,删完了的情况就行了。

    注意long long

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <ctime>
#define inf 1e17
#define Int long long
using namespace std;
Int a,b,Dp[1000010][3],Ans = inf;
int n,A[1000010],c[10];
Int calc(int x,int M) {
	if(x % M == 0) return 0;
	if((x + 1) % M == 0 || ((x - 1) % M == 0 && x >= 2)) return b;
	return inf;
}
Int work(int M) {
	for(int i = 1;i <= n;i ++)
	{
		Dp[i][0] = inf;
		Dp[i][1] = inf;
		Dp[i][2] = inf;
		Dp[i][0] = Dp[i - 1][0] + calc(A[i],M);
		Dp[i][1] = min(Dp[i - 1][1],Dp[i - 1][0]) + a;
		Dp[i][2] = min(Dp[i - 1][1],Dp[i - 1][2]) + calc(A[i],M);
		Dp[i][0] = (Dp[i][0] > inf) ? inf:Dp[i][0];
		Dp[i][1] = (Dp[i][1] > inf) ? inf:Dp[i][1];
		Dp[i][2] = (Dp[i][2] > inf) ? inf:Dp[i][2];
	}
	Int ret = inf;
	ret = min(ret,min(Dp[n][0],Dp[n][2]));
	if(Dp[n][1] != n * a) ret = min(ret,Dp[n][1]);
	return ret;
}
int main() {
	scanf("%d",&n);
	scanf("%I64d%I64d",&a,&b);
	for(int i = 1;i <= n;i ++) scanf("%d",&A[i]);
	c[1] = A[1];c[2] = A[n];
	c[3] = A[1] - 1;c[4] = A[1] + 1;
	c[5] = A[n] - 1;c[6] = A[n] + 1;
	for(int i = 1;i <= 6;i ++)
	{
		for(int p = 2;p * p <= c[i];p ++)
		{
			if(c[i] % p == 0)
			{
				while(c[i] % p == 0) c[i] /= p;
				Ans = min(Ans,work(p));
			}
		}	
		if(c[i] != 1) Ans = min(Ans,work(c[i]));	
	}
	printf("%I64d",Ans);
}


    C:题意:给出n个点(n<=100000),每个点用坐标表示,现在对于每个点,你可以把它的横坐标或者纵坐标变成0,问通过这样的操作,两两点之间的距离最大值最小是多少。

    这道题真的是调死了,首先有一个显而易见的性质,如果我们把第i个点投在横轴上,那么对于所有的点j,只要有|x[j]|<=|x[i]|,我们就也把它投到横轴上,因为这样一来计算答案的时候肯定还是用i,但是又减少了纵轴的点,只好不坏。

    然而还是不太好做的样子,因为除了横轴与纵轴的关系,同一根轴上的点之间也可能有距离,想到这个就可以想到二分答案了,我们二分最长的距离(其实求最大值最小本来就很可能是二分答案),然后对于每个点i,考虑把它投在横轴上的情况,如果此时x[i]<=0,那么我们找到所有的x[j]>x[i]&&dis[j][i]<Ans,把这些点投在横轴上,再把剩下的点投在纵轴上。我们再考虑x[i]>0,那么我们找到所有的x[j]<x[i]&&dis[j][i]<Ans,把这些点投在横轴上。(以上两种情况都还有一个条件,就是所有的点j都还必须满足|x[j]|<=|x[i]|,因为j点不能影响到i点对答案的计算),我们只要用个队列就可以实现on)求每个点在当前Ans下的答案了(我还记录了前缀minmax,后缀minmax)。

    越调试越复杂,代码也是越调越丑....

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <ctime>
#define inf 2ll * 1e18
#define Int long long
using namespace std;
struct point{Int x;Int y;
};point Point[100010];
int n,m;
Int Ans = 1e18,pre_maxx[100010],bac_maxx[100010];
Int bac_minx[100010],pre_minx[100010];
bool comp(const point &x,const point &y) {
	if(x.x != y.x) return x.x < y.x;
	return x.y > y.y;
}
bool check(Int x) {
	int r = 0,s = 0,l = 1;
	for(int i = 1;i <= n;i ++)
	{
		if(Point[i].x >= 0) {s = i;break;}
		while(r < i) r ++;
		while(r < n && (Point[r + 1].x - Point[i].x) * (Point[r + 1].x - Point[i].x) <= x && abs(Point[r + 1].x) <= abs(Point[i].x)) r ++;
		while(r > i && abs(Point[r].x) > abs(Point[i].x)) r --;
		if(i == 1 && r == n) return true;
		else 
		{
			Int c = max(pre_maxx[i - 1],bac_maxx[r + 1]);
			Int d = min(pre_minx[i - 1],bac_minx[r + 1]);
			Int e = max(abs(Point[i].x),abs(Point[r].x));
			if((c - d) * (c - d) <= x && e * e + d * d <= x && e * e + c * c <= x)
				return true;
		} 
	}
	if(s == 0) s = n + 1;
	for(int i = s;i <= n;i ++)
	{
		while(l < i && (Point[l].x - Point[i].x) * (Point[l].x - Point[i].x) > x) l ++;
		while(l < i && abs(Point[l].x) > abs(Point[i].x)) l ++;
		while(l > 1 && abs(Point[l - 1].x) <= abs(Point[i].x) && (Point[l - 1].x - Point[i].x) * (Point[l - 1].x - Point[i].x) <= x) l --;
		if(i == n && l == 1) return true;
		else 
		{
			Int c = max(pre_maxx[l - 1],bac_maxx[i + 1]);
			Int d = min(pre_minx[l - 1],bac_minx[i + 1]);
			Int e = max(abs(Point[l].x),abs(Point[i].x));
			if((c - d) * (c - d) <= x && e * e + d * d <= x && e * e + c * c <= x)
				return true;
		} 
	}
	return false;
}
int main() {
	scanf("%d",&n);
	for(int i = 1;i <= n;i ++) 
		scanf("%I64d%I64d",&Point[i].x,&Point[i].y);
	sort(Point + 1,Point + n + 1,comp);
	bac_maxx[n + 1] = -1ll * inf;
	bac_minx[n + 1] = inf;
	Int A,B,C,D;
	A = C = -1ll * inf;
	B = D = inf;
	for(int i = n;i >= 1;i --) 
	{
		A = max(A,Point[i].x);
		B = min(B,Point[i].x);
		C = max(C,Point[i].y);
		D = min(D,Point[i].y);
		bac_maxx[i] = max(Point[i].y,bac_maxx[i + 1]);
		bac_minx[i] = min(Point[i].y,bac_minx[i + 1]);
	}
	bac_maxx[0] = bac_maxx[1];
	bac_minx[0] = bac_minx[1];
	pre_maxx[1 - 1] = -1ll * inf;
	pre_minx[1 - 1] = inf;
	for(int i = 1;i <= n;i ++) 
	{
		pre_maxx[i] = max(Point[i].y,pre_maxx[i - 1]);
		pre_minx[i] = min(Point[i].y,pre_minx[i - 1]);
	}
	pre_maxx[n + 1] = pre_maxx[n];
	pre_minx[n + 1] = pre_minx[n];
	Int head = 0,tail = inf,T = 100;
	while(head != tail)
	{
		Int Mid = (head + tail) >> 1;
		if(check(Mid)) tail = Mid;
		else head = Mid + 1;
	}
	cout<<min(head,min((B - A) * (B - A),(D - C) * (D - C)));
	return 0;
}


    D:i个人,抓到第i个人的几率是百分之a[i],现在你每抓一个人就要猜一次这个人是谁,游戏结束当且仅当每个人你都曾抓到并且猜中是他。问期望多少步游戏结束。(n<=100

    这道题反而压力没有上一道题那么大,一个概率问题的通用想法,算出很多步之后的答案,再往后的因为太小就可以忽略了,这道题也是这样,A[i]表示在第i步结束的概率,我们再设p[i]为抓到i的概率,q[i]=1-p[i],k[i]为猜抓到的人是i的次数。其中有A[t]=(1-q[1]^k[1])*(1-q[2]*k[2])....(1-q[n]*k[n]).,其中k[1]+k[2]+...k[n]=t。(就相当于用1减去每次猜i都没有猜中的几率,就是至少猜中了一次的几率了)。

    那么答案=(A[1]-A[0])*1+(A[2]-A[1])*2+.....(A[T]-A[T-1])*TT是自己定的常数。

    最后的问题就在于如何把t分在每一个k[]里使得A[t]最大,其实这个贪心就可以了,(很明显的吧..),我们看当前(1-q[i]*(1-p[i])) / (1-q[i])最大的那个i,把它的q[i]乘上(1-p[i])就可以了。

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
double f[300010],p[110],q[110],k[110],Ans = 0;
int n,T;
int main() {
	scanf("%d",&n);
	for(int i = 1;i <= n;i ++) cin>>p[i],q[i] = 1.0;
	for(int i = 1;i <= n;i ++) p[i] /= 100.0;
	T = 500000;
	double last = 0;
	for(int i = 0;i <= T;i ++) 
	{
		double x = 1,ret = -100.0;int M;
		for(int j = 1;j <= n;j ++) x = x * (1.0 - q[j]);
		Ans += (x - last) * (double)i;
		last = x;
		for(int j = 1;j <= n;j ++)
			if((1.0 - q[j] * (1.0 - p[j])) / (1.0 - q[j]) > ret)
			{
				ret = (1.0 - q[j] * (1.0 - p[j])) / (1.0 - q[j]);
 				M = j;
			}
		q[M] *= (1.0 - p[M]); 
	}
	printf("%0.10f",Ans);
	return 0;
}


    E题多项式,弃掉.....

话说这场真的好难,幸好当时没打...

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值