AIM Tech Round Div 1

来源:互联网 发布:手写输入法软件 编辑:程序博客网 时间:2024/06/04 20:09

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 longusing 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 longusing 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