[Codeforces #295(Div 1)]简要题解

A. DNA Alignment

Vasya became interested in bioinformatics. He’s going to write an article about similar cyclic DNA sequences, so he invented a new method for determining the similarity of cyclic sequences.

Let’s assume that strings s and t have the same length n, then the function h(s, t) is defined as the number of positions in which the respective symbols of s and t are the same. Function h(s, t) can be used to define the function of Vasya distance ρ(s, t):
这里写图片描述
where is obtained from string s, by applying left circular shift i times. For example,
ρ(“AGC”, “CGT”) = 
h(“AGC”, “CGT”) + h(“AGC”, “GTC”) + h(“AGC”, “TCG”) + 
h(“GCA”, “CGT”) + h(“GCA”, “GTC”) + h(“GCA”, “TCG”) + 
h(“CAG”, “CGT”) + h(“CAG”, “GTC”) + h(“CAG”, “TCG”) = 
1 + 1 + 0 + 0 + 1 + 1 + 1 + 0 + 1 = 6
Vasya found a string s of length n on the Internet. Now he wants to count how many strings t there are such that the Vasya distance from the string s attains maximum possible value. Formally speaking, t must satisfy the equation: 这里写图片描述.

Vasya could not try all possible strings to find an answer, so he needs your help. As the answer may be very large, count the number of such strings modulo 109+7 .

Input
The first line of the input contains a single integer n (1n105).

The second line of the input contains a single string of length n, consisting of characters “ACGT”.

Output
Print a single number — the answer modulo 109+7 .

题目大意
对于字符串 st ,定义 ρ(s,t) 如下:
这里写图片描述
如:
ρ("AGC","CGT")=
h("AGC","CGT")+h("AGC","GTC")+h("AGC","TCG")+
h("GCA","CGT")+h("GCA","GTC")+h("GCA","TCG")+
h("CAG","CGT")+h("CAG","GTC")+h("CAG","TCG")=
1+1+0+0+1+1+1+0+1=6
给出长度为 ns ,求有多少个不同的字符串 t 满足这里写图片描述

题解
其实这个题非常的水。。。但是它的题面长,而且四个样例都很特殊,难以发现特点,坑得我一直没做出来。实际上可以发现,使得ρ(s,t)最大的 t 串,其中的所有字母一定都是出现次数最多的字母。那么我们就相当于构造一个长度为n的字符串,这个串中的每个字母都是原来 s 中出现次数最多的字母。由于s串中出现次数最多的字母可能不止一个,因此我们首先要找出 s 中有多少种出现次数最多的字母,设共tot种。因为这个字符串只能由A、T、C、G四种字母组成,因此最多有四种出现次数最多的字母。然后构造长度为 nt ,每个位置可以选填 tot 种字母中的一个,因此最终的答案是 totn mod (109+7)

#include <iostream>
#include <stdio.h>
#include <stdlib.h>

#define MAXN 110000
#define MOD 1000000007

using namespace std;

typedef long long int LL;

int n;
char s[MAXN],tmp[MAXN];

LL mul(LL a,LL b)
{
    LL ans=0;
    while(b)
    {
        if(b&1) ans=(ans+a)%MOD;
        a=(a+a)%MOD;
        b>>=1;
    }
    return ans;
}

LL fastPow(LL base,int pow)
{
    LL ans=1;
    while(pow)
    {
        if(pow&1) ans=mul(ans,base);
        base=mul(base,base);
        pow>>=1;
    }
    if(ans<0) ans+=MOD;
    return ans;
}

int sum[MAXN];

int main()
{
    scanf("%d",&n);
    scanf("%s",s+1);
    int maxsum=-1,tot=0;
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='A') sum[1]++;
        else if(s[i]=='G') sum[2]++;
        else if(s[i]=='C') sum[3]++;
        else sum[4]++;
    }
    for(int i=1;i<=4;i++)
        if(sum[i]>maxsum)
            maxsum=sum[i];
    for(int i=1;i<=4;i++)
        if(sum[i]==maxsum)
            tot++;
    printf("%I64d\n",fastPow((LL)tot,n));
    return 0;
}

B. Cubes

Once Vasya and Petya assembled a figure of m cubes, each of them is associated with a number between 0 and m - 1 (inclusive, each number appeared exactly once). Let’s consider a coordinate system such that the OX is the ground, and the OY is directed upwards. Each cube is associated with the coordinates of its lower left corner, these coordinates are integers for each cube.

The figure turned out to be stable. This means that for any cube that is not on the ground, there is at least one cube under it such that those two cubes touch by a side or a corner. More formally, this means that for the cube with coordinates (x, y) either y = 0, or there is a cube with coordinates (x - 1, y - 1), (x, y - 1) or (x + 1, y - 1).

Now the boys want to disassemble the figure and put all the cubes in a row. In one step the cube is removed from the figure and being put to the right of the blocks that have already been laid. The guys remove the cubes in such order that the figure remains stable. To make the process more interesting, the guys decided to play the following game. The guys take out the cubes from the figure in turns. It is easy to see that after the figure is disassembled, the integers written on the cubes form a number, written in the m-ary positional numerical system (possibly, with a leading zero). Vasya wants the resulting number to be maximum possible, and Petya, on the contrary, tries to make it as small as possible. Vasya starts the game.

Your task is to determine what number is formed after the figure is disassembled, if the boys play optimally. Determine the remainder of the answer modulo 109+9 .

Input
The first line contains number m(2m105) .

The following m lines contain the coordinates of the cubes xi,yi(109xi109,0yi109) in ascending order of numbers written on them. It is guaranteed that the original figure is stable.

No two cubes occupy the same place.

Output
In the only line print the answer to the problem.

题目大意
给出 n 个方块以及它们的坐标,第i个方块的标号是 i 。定义一个方块是稳定的,当且仅当它是在地面上(y坐标为0)或者它与下面的一个方块八连通,现在两个人要在这个坐标系上做游戏,每次取一个方块,并保证取完以后剩下的方块仍然都是稳定的,取完 n 次后,将n次取的方块的标号依次排列起来形成一个 nn 进制数。先手想让这个数尽量大,后手想让这个数尽量小,双方均采用最优策略,问最后这个数字是多少
思路
比较显然,先手一定是尽量取大标号的方块,后手一定是尽量取小标号的方块,我们可以用一个set来维护一个优先队列(为什么不用Priority queue?因为它不能访问队尾的元素,很麻烦),set可以取队首的最小元素,也可以取队尾的最大元素,符合题目需求。那么我们就要时刻保证set中的所有方块,删去任意一个都不会破坏当前剩下的方块的稳定性。
然后就是模拟先手和后手轮流下游戏的过程了。每次删掉某个方块后,要更新可能受到它影响的方块是否还能在下一次操作中被删去。具体的范围是 (x2,y2)(x+2,y+2)
一个很明显的例子,比如说
.1.
2.3
假如现在删去3,那么就会导致下一步2不能删。
具体细节看代码吧,注释说明得很清楚

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <set>
#include <map>
#include <queue>

#define MAXN 110000
#define MOD 1000000009

using namespace std;

typedef unsigned long long int ULL;

int n;
ULL ans=0;

struct Point
{
    int x,y; //某个方块的坐标
    Point(){}
    Point(int _x,int _y):x(_x),y(_y){}
}points[MAXN];

bool operator<(Point a,Point b)
{
    if(a.x==b.x) return a.y<b.y;
    return a.x<b.x;
}

bool operator>(Point a,Point b)
{
    if(a.x==b.x) return a.y>b.y;
    return a.x>b.x;
}

map<Point,ULL>mp; //mp[i]=点i的标号,为0表示点i已经被用过了
set<ULL>heap; //用set来模拟一个像堆一样的神奇数据结构

int check(int x,int y) //检查(x,y)的下面有多少个格子与它联通
{
    int ans=0;
    for(int i=-1;i<=1;i++)
        if(mp[Point(x+i,y-1)])
            ans++;
    return ans;
}

void update(int x,int y) //删去(x,y)后,检查受他影响的方块是否还是稳定的
{
    bool flag=true; //flag=true表示可以拆(x,y) (拆了(x,y)后受他影响的点仍然稳定)
    for(int i=-1;i<=1;i++)
        if(mp[Point(x+i,y+1)]&&check(x+i,y+1)==1) //拆了(x,y)后,他下面八连通的格子中有变成不稳定的格子
            flag=false;
    if(!flag) //不能删去(x,y),将它从heap中删去
    {
        if(heap.count(mp[Point(x,y)]))
                heap.erase(mp[Point(x,y)]);
    }
    else //可以删去(x,y),把它加入heap
        if(!heap.count(mp[Point(x,y)]))
            heap.insert(mp[Point(x,y)]);
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&points[i].x,&points[i].y);
        mp[points[i]]=i;
    }
    for(int i=1;i<=n;i++) //初始时把所有可以删的点都放入heap
        update(points[i].x,points[i].y);
    for(int i=1;i<=n;i++)
    {
        int p; //p=要删除的点的标号
        if(i&1) //先手,删标号最大的点
        {
            p=*heap.rbegin();
            heap.erase(p);
            ans=(ans*n+p-1)%MOD;
        }
        else //后手,删标号最小的点
        {
            p=*heap.begin();
            heap.erase(p);
            ans=(ans*n+p-1)%MOD;
        }
        mp[points[p]]=0; //标记点p已经用过
        for(int i=-2;i<=2;i++) //在x-2,y-2到x+2,y+2范围内检查这些点还能否删去
            for(int j=-2;j<=2;j++)
                if(mp[Point(points[p].x+i,points[p].y+j)])
                    update(points[p].x+i,points[p].y+j);
    }
    printf("%I64u\n",ans);
    return 0;
}

C. Pluses everywhere

Vasya is sitting on an extremely boring math class. To have fun, he took a piece of paper and wrote out n numbers on a single line. After that, Vasya began to write out different ways to put pluses (“+”) in the line between certain digits in the line so that the result was a correct arithmetic expression; formally, no two pluses in such a partition can stand together (between any two adjacent pluses there must be at least one digit), and no plus can stand at the beginning or the end of a line. For example, in the string 100500, ways 100500 (add no pluses), 1+00+500 or 10050+0 are correct, and ways 100++500, +1+0+0+5+0+0 or 100500+ are incorrect.

The lesson was long, and Vasya has written all the correct ways to place exactly k pluses in a string of digits. At this point, he got caught having fun by a teacher and he was given the task to calculate the sum of all the resulting arithmetic expressions by the end of the lesson (when calculating the value of an expression the leading zeros should be ignored). As the answer can be large, Vasya is allowed to get only its remainder modulo 109+7 . Help him!

Input
The first line contains two integers, n and k (0k<n105) .

The second line contains a string consisting of n digits.

Output
Print the answer to the problem modulo 109+7 .
题目大意
给出一个长度为 n 的数字序列,用k个加号将它分割成若干个数字,并得到这个算式的最终答案(数字和),求所有划分方案的答案之和。
1082 种划分方案: (10)+81+(08) ,所有方案的算式答案之和为 18+9=27
题解
为了方便叙述,以下说第 i 位就是说从低位到高位数的第i
注意到对于第 1n 位,每一位数字在最终的划分方案中充当中间的一个划分块的个位的次数是相同的,充当中间的一个划分块的十位的次数也是相同的……由此我们可以枚举所有数字在最终答案中充当中间的一个划分块的第 i 位所提供的贡献。每个数字在最终答案中充当中间一个划分块的第i位的次数是 Ck1(k1)ni1(ni1)
所有数字在最终答案中充当xxxx的第 i 位参与的贡献是sum[ni]×10i1×Ck1ni1,sum[i]1i位数位(第i位到最高位数位)的前缀和(因为个位到第i-1位不能充当一个块中的第i位,所以不能算入贡献)。
然后还有特殊情况:对于最右边的划分块而言,它的右边是没有加号的,加号只能往左边放,里面的数字 x 充当这个划分块第i位的贡献是 x×10i1×Ckni

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 110000
#define MOD 1000000007

using namespace std;

typedef long long int LL;

char s[MAXN];
LL sum[MAXN],pow[MAXN]; //pow[i]=10^i
LL fact[MAXN]; //fact[i]=i!
LL ans=0;

LL extGCD(LL a,LL b,LL &x,LL &y) //ax+by=gcd(a,b)
{
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    LL gcd=extGCD(b,a%b,x,y);
    LL t=x;
    x=y;
    y=t-a/b*y;
    return gcd;
}

LL rev(LL a,LL mod) //求a在模mod意义下的乘法逆元
{
    LL x,y;
    extGCD(a,mod,x,y);
    return (x%mod+mod)%mod;
}

LL C(LL n,LL m) //n个里选m个
{
    if(n<m) return 0;
    return fact[n]*rev(fact[m],MOD)%MOD*rev(fact[n-m],MOD)%MOD;
}

int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
        sum[i]=(sum[i-1]+s[i]-'0')%MOD;
    if(!k) //特判无加号的情况
    {
        for(int i=1;i<=n;i++)
            ans=(ans*10+s[i]-'0')%MOD;
        printf("%I64d\n",ans);
        return 0;
    }
    fact[0]=1;
    for(int i=1;i<=n;i++) fact[i]=(fact[i-1]*i)%MOD;
    pow[0]=1;
    for(int i=1;i<=n;i++) pow[i]=(pow[i-1]*10)%MOD;
    for(int i=1;i<=n;i++) //枚举所有数在第i位的总贡献
    {
        ans=(ans+(LL)(sum[n-i])*pow[i-1]%MOD*C(n-i-1,k-1))%MOD;
        ans=(ans+(LL)(s[n-i+1]-'0')*pow[i-1]%MOD*C(n-i,k))%MOD;
    }
    printf("%I64d\n",ans);
    return 0;
}

D. Shop

Vasya plays one very well-known and extremely popular MMORPG game. His game character has k skill; currently the i-th of them equals to ai. Also this game has a common rating table in which the participants are ranked according to the product of all the skills of a hero in the descending order.

Vasya decided to ‘upgrade’ his character via the game store. This store offers n possible ways to improve the hero’s skills; Each of these ways belongs to one of three types:

assign the i-th skill to b;
add b to the i-th skill;
multiply the i-th skill by b.
Unfortunately, a) every improvement can only be used once; b) the money on Vasya’s card is enough only to purchase not more than m of the n improvements. Help Vasya to reach the highest ranking in the game. To do this tell Vasya which of improvements he has to purchase and in what order he should use them to make his rating become as high as possible. If there are several ways to achieve it, print any of them.

Input
The first line contains three numbers — k,n,m(1k105,0mn105) — the number of skills, the number of improvements on sale and the number of them Vasya can afford.

The second line contains k space-separated numbers ai (1ai106), the initial values ​​of skills.

Next n lines contain 3 space-separated numbers tj,ij,bj (1tj3,1ijk,1bj106) — the type of the j-th improvement (1 for assigning, 2 for adding, 3 for multiplying), the skill to which it can be applied and the value of b for this improvement.

Output
The first line should contain a number l (0lm) — the number of improvements you should use.

The second line should contain l distinct space-separated numbers vi (1vin) — the indices of improvements in the order in which they should be applied. The improvements are numbered starting from 1, in the order in which they appear in the input.

题目大意
某人有 k 个技能a1...ak,定义这些技能的总得分为它们的乘积。现在这个人想要到商店里升级自己的技能,有三种升级方法:
1、给某个技能 ai 赋一个初值 b
2、给某个技能ai加上值 b
3、给某个技能ai乘上值 b
现在一共有n个升级方法供这个人购买,这个人最多只能选其中的 m 个,给出这些升级方法的类型、被操作的技能以及值bi。问该选择哪些升级,以及按照什么样的顺序升级才能使最终的技能总得分最高。只要求按照升级类型为第一关键字、升级方法的编号为第二关键字输出选择的升级方法。
题解
首先copy来毛子的英文题解(能看懂原文题解的就忽略蒟蒻我的题解吧,怕误人子弟。。。)

Suppose the only type of upgrades we have is multiplication. It
doesn’t even matter for the answer which particular skill we are going
to multiply, so we just choose several upgrades with greatest values
of bi.

Now we have additions as well; set multiplications aside for a moment.
It is clear that for every skill we should choose several largest
additions (maybe none). Let us sort the additions for every skill by
non-increasing; now we should choose several first upgrades for each
type. Now, for some skill the (non-increasing) sorted row of b’s is
b1, …, bl, and the initial value of the skill is a. Now, as we have
decided to take some prefix of b’s, we know that if we take the
upgrade bi, the value changes from a + b1 + … + bi - 1 to
a + b1 + … + bi - 1 + bi. That is, the ratio by which the value (and
the whole product of values) is going to be multiplied by is the
fraction . Now, with that ratio determined unambigiously for each
addition upgrade, every addition has actually become a multiplication.
=) So we have to compute the ratios for all additions (that is, we sort b’s for each skill separately and find the fractions), and then
sort the multiplications and additions altogether by the ratio they
affect the whole product with. Clearly, all multiplications should be
used after all the additions are done; that is, to choose which
upgrades we use we should do the ratio sorting, but the order of
actual using of upgrades is: first do all the additions, then do all
the multiplications.

Finally, let’s deal with the assignment upgrades. Clearly, for each
skill at most one assignment upgrade should be used, and if it used,
it should the assignment upgrade with the largest b among all
assignments for this skill. Also, if the assignment is used, it should
be used before all the additions and multiplications for this skill.
So, for each skill we should simply determine whether we use the
largest assignment for this skill or not. However, if we use the
assignment, the ratios for the additions of current skill become
invalid as the starting value of a is altered.

To deal with this problem, imagine that we have first chosen some
addition upgrades, and now we have to choose whether we use the
assignment upgrade or not. If we do, the value of the skill changes
from a + b1 + … + bk to b + b1 + … + bk. That is, the assignment
here behaves pretty much the same way as the addition of b - a. The
only difference is that once we have chosen to use the assignment, we
should put it before all the additions.

That is, all largest assigments for each skill should be made into
additions of b - a and processed along with all the other additions,
which are, as we already know, going to become multiplications in the
end. =)

Finally, the problem is reduced to sorting the ratios for all
upgrades. Let us estimate the numbers in the fractions. The ratio for
a multiplication is an integer up to 106; the ratio for an addition is
a fraction of general form . As k can be up to 105, and bi is up to
106, the numerator and denominator of such fraction can go up to 1011.
To compare fractions and we should compare the products ad and bc,
which can go up to 1022 by our estimates. That, unfortunately,
overflows built-in integer types in most languages. However, this
problem can be solved by subtracting 1 from all ratios (which clearly
does not change the order of ratios), so that the additions’ ratios
will look like . Now, the numerator is up to 106, the products in the
comparison are up to 1017, which fits in 64-bit integer type in any
language.

显然,由于最终的总得分是各个技能的乘积,因此进行乘法的升级方法可以忽略它们的被操作对象,这是个很不错的性质。然后赋初值的升级操作也可以看成是加上值 biai 的升级方法,将它们加入到对应的被升级对象的加法升级序列中。因此最终问题就只剩下了乘法升级和加法升级操作了。
然后发现如果将加法升级和乘法升级共同使用的话,一定是先把所有的加法升级进行完,然后再将所有乘法升级进行完是最优的。所以最开始只有加法操作,没有乘法操作,注意到在这种特殊情景下,加法升级是同样可以写成乘法升级的,假设对 ai 加上某个值 bi ai+bi=aiai+biai 。因为在一堆加法升级里选一部分出来,肯定是选加的 bi 最多的那些升级方法。这样一来我们就可以对每个技能对应的加法升级按 bi 进行降序排序,然后将这些加法升级转换成乘法升级,加入到原有的乘法升级的序列中。
然后乘法升级的话是不需要管被操作对象的,我们就对所有的乘法升级按照 bi 降序排序,然后从大到小选尽量多的乘法升级就行了。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <vector>

#define MAXN 110000

using namespace std;

int a[MAXN]; //a[i]=技能i的初值
int b[MAXN][3]; //b[i][0]=第i个升级的类型,info[i][1]=第i个升级的对象,info[i][2]=第i个升级的值b
int c[MAXN][2]; //c[i][0]=第i个技能的最大赋初值操作的b值,c[i][1]=c[i][0]操作对应的操作编号
int k,n,m;
vector<pair<int,int> >add[MAXN]; //存放每个技能对应的可以选择的赋初值操作以及b值
vector<pair<double,int> >mul; //存放的是每个技能可选择的乘法操作
vector<pair<int,int> >sol; //最终的操作序列,first是操作类型,second是1~n中操作的编号

int main()
{
    scanf("%d%d%d",&k,&n,&m);
    for(int i=1;i<=k;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&b[i][0],&b[i][1],&b[i][2]);
    for(int i=1;i<=n;i++)
    {
        if(b[i][0]==1) //赋初值操作
        {
            int x=b[i][1],val=b[i][2]; //val是操作的b值,x是被操作对象
            if(c[x][0]<val)
            {
                c[x][0]=val;
                c[x][1]=i;
            }
        }
        else if(b[i][0]==2) //加法操作
        {
            int x=b[i][1],val=b[i][2]; //val是操作的b值,x是被操作对象
            add[x].push_back(make_pair(val,i));
        }
        else //乘法操作
        {
            int x=b[i][1],val=b[i][2]; //val是操作的b值,x是被操作对象
            mul.push_back(make_pair(val,i));
        }
    }
    for(int i=1;i<=k;i++)
    {
        double sum=a[i];
        if(c[i][0]>a[i]) add[i].push_back(make_pair(c[i][0]-a[i],c[i][1]));
        //cout<<i<<' '<<add[i].size()<<endl;
        sort(add[i].begin(),add[i].end());
        reverse(add[i].begin(),add[i].end());
        for(int j=0;j<add[i].size();j++)
        {
            mul.push_back(make_pair((sum+add[i][j].first)/sum,add[i][j].second)); //把加法操作转化成乘法操作放入乘法操作的vector里
            sum+=add[i][j].first;
        }
    }
    //cout<<mul.size()<<endl;
    sort(mul.begin(),mul.end());
    reverse(mul.begin(),mul.end());
    for(int i=0;i<mul.size()&&i<m;i++)
        sol.push_back(make_pair(b[mul[i].second][0],mul[i].second));
    printf("%d\n",sol.size());
    sort(sol.begin(),sol.end());
    for(int i=0;i<sol.size();i++)
        printf("%d ",sol[i].second);
    puts("");
    return 0;
}

E

IGM大爷专属的神题,渣渣我不会做!
TKD大爷说就是要判断输入的一张图是不是仙人掌就行了,我不知道是否正确,大家还是去看tutorial吧

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值