Noip2015普及组

T3:

题目:https://jzoj.net/junior/#main/show/2021


这道题的题目大意就是让你从一个数列中找到一种三元组,使得三元组满足两个条件:

①  等差:y-x=z-y

②  Color[x]=color[y]

如果满足的话就计算(x+z)*(number[x]+number[z])的值。

20分:首先想到的方法,应该是最简单的方法就是三重循环,枚举x,y,z如果条件成立,就inc(ans,(x+z)*(num[x]+num[z])),但这种方法效率太低,O(n3),大概只能得20分。

40分:进一步想,可以发现规律,其实等差这个条件只要满足(x+z) mod 2 =0 便可以了,又因数值计算时完全与y无关,所以我们只用枚举x,z就可以了。

100分:上面的方法虽然已经将效率从O(n3)转换成了O(n2),但是依然得不到满分。

现在我们假设数据是这样子的:

     6 2

     5 5 3 2 2 2 2

     2 2 1 1 2 1 2

Num: 1 2 3 4 5 6 7

X=5 y=2 z=2

我们可以发现,其实第1个数,第5个数,第7个数,他们是可以互相组成3元组的,注意:是互相。也就是1-5,1-7,5-7.

为什么他们可以互相组成三元组呢?因为他们都是奇数,符合等差要求,又同一颜色,所以他们全都可以互相组成三元组。

他们数值的计算为(设第1个数为x,第2个数为y,第三个数为z)

(x+y)*(num[x]+num[y])+

(x+z)*(num[x]+num[z])+

(y+z)*(num[y]+num[z])

拆分之:X*(num[x]+num[x]+num[y]+num[z])+

        Y*(num[x]+num[y]+num[y]+num[z])+

        Z*(num[x]+num[y]+num[z]+num[z])

所以这个式子最终可以变成(x+y+z)*(num[x]+num[y]+num[z])+(x*num[x]+y*num[y]+z*num[z])。所以我们只需用3个数组对对应的数存一下就行了。


然后我们再看一个栗子:

假设4个数分别为,x1,x2,x3,x4他们的num[x1..x4]=n1,n2,n3,n4

那么

(x1+x2)*(n1+n2)+

(x1+x3)*(n1+n3)+

(x1+x4)*(n1+n4)+

(x2+x3)*(n2+n3)+

(x2+x4)*(n2+n4)+

(x3+x4)*(n3+n4)。

所以这个式子也可简化成:

x1*(n1*3+n2+n3+n4)+

x2*(n1+n2*3+n3+n4)+

x3*(n1+n2+n3*3+n4)+

x4*(n1+n2+n3+n4*3)。

变成(x1+x2+x3+x4)*(n1+n2+n3+n4)+2(x1*n1)+2(x2*n2)+2(x3*n3)+2(x4*n4),所以我们发现,当有4个数的时候,乘法分配律的结果会使原本的(a*b)*(num[a]+num[b])再乘多一个2.所以当我们有n个数的时候,则上面这个长串式子第二个右括号后面的加号的一连串的数要乘以(n-2)即可。

代码:

const
        maxn=10007;
var
        n,m,i,ans:longint;
        num,col,cnt,a,b,c:array[1..100000] of int64;
function f(k:longint):longint;
begin
        fillchar(cnt,sizeof(cnt),0);
        fillchar(a,sizeof(a),0);
        fillchar(b,sizeof(b),0);
        fillchar(c,sizeof(c),0);
        f:=0;

        while k<=n do
        begin
                inc(cnt[col[k]]);
                inc(a[col[k]],k*num[k]);
                inc(b[col[k]],num[k]);
                inc(c[col[k]],k);
                inc(k,2);
        end;
        for i:=1 to m do
                if cnt[i]>1 then f:=(f+(cnt[i]-2)*a[i]+b[i]*c[i]) mod maxn;
end;
begin
        assign(input,'sum.in');reset(input);
        assign(output,'sum.out');rewrite(output);

        readln(n,m);
        for i:=1 to n do
                read(num[i]);
        for i:=1 to n do
                read(col[i]);

        writeln((f(1)+f(2)) mod maxn);

        close(input);close(output);
end.



T4:

题目:https://jzoj.net/junior/#main/show/2022


这道题其实是一道贪心的题目,我们把a,b数组列出来:

A=1 2 4 3 5

B=5 4 4 3 1,这显然是第二个样例当中以b数组从大到小排序后得到的数组,其实我们贪心就是有两种情况。那就是判断第x家推不推销而已。

例如,当我们求推销x=3家的时候。我们只有两种情况,那就是把前三家都推销了,还有就是推销前两家,再推销最后三家的最大值。什么意思呢?

设到第i家时,max=当前a[1..i]的最大值,sum=b[1..i]的和,然后max*2+sum=ans1也就是前三家都选(贪心的时候我们可以先确定前i-1家是必选的,第i家只是选一个使得最后结果更多的一家),还有就是sum-b[i]+max{c[i..n]}=ans2,这个max{c[i..n]}指的是倒数i家的单个推销的最大值。

所以这两者之间的答案一定是当前最优解,因为对于当前新加的一个推销,就是判断是i~n的哪一家,因为前i-1家是必选的。

而如果选第i家,则当前值=ans1,而如果以第i+1~n家中一家为最远的一家得到的答案则等于ans2,现在应该知道为什么要ans2了吧,因为当当前i+1~n家中一家为结尾的话,有可能会得到更大的值,当然换一句话说,也就是可能i+1~n中的a[i]大于max非常多,以至于最优解都变了,当这种情况发生的时候贪心就起到效果了。

代码:

type
        arr=array[0..100000] of longint;
var
        a,b,cnt:arr;
        n,i,p,ans,sum,temp:longint;
function max(x,y:longint):longint;
begin
        if x>y then exit(x) else exit(y);
end;
procedure change(x,y:longint; var t:arr);
begin
        temp:=t[x]; t[x]:=t[y]; t[y]:=temp;
end;
procedure sort(l,r:Longint);
var
        i,j,mid,p:longint;
begin
        i:=l; j:=r;
        mid:=b[(l+r) div 2];
        while imid do inc(i);
                while b[j]


你可能感兴趣的:(比赛题解)