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.
题目: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 i<j do begin while b[i]>mid do inc(i); while b[j]<mid do dec(j); if i<=j then begin change(i,j,a); change(i,j,b); inc(i); dec(j) end; end; if l<j then sort(l,j); if i<r then sort(i,r); end; begin assign(input,'salesman.in'); reset(input); assign(output,'salesman.out'); rewrite(output); readln(n); for i:=1 to n do read(a[i]); for i:=1 to n do read(b[i]); sort(1,n); for i:=n downto 1 do cnt[i]:=max(cnt[i+1],a[i]*2+b[i]); for i:=1 to n do begin inc(sum,b[i]); p:=max(p,a[i]); writeln(max(sum+p*2,sum-b[i]+cnt[i])); end; close(input); close(output); end.