Cut Pieces (数学分析---杭电第六场)

来源:互联网 发布:python源代码 pass 编辑:程序博客网 时间:2024/05/16 15:06

【题目链接】http://acm.hdu.edu.cn/showproblem.php?pid=4655

 

Problem Description
Suppose we have a sequence of n blocks. Then we paint the blocks. Each block should be painted a single color and block i can have color 1 to color ai. So there are a total of prod(ai)  different ways to color the blocks.
Consider one way to color the blocks. We call a consecutive sequence of blocks with the same color a "piece". For example, sequence "Yellow Yellow Red" has two pieces and sequence "Yellow Red Blue Blue Yellow" has four pieces. What is S, the total number of pieces of all possible ways to color the blocks?

This is not your task. Your task is to permute the blocks (together with its corresponding ai) so that S is maximized.
 
Input
First line, number of test cases, T.
Following are 2*T lines. For every two lines, the first line is n, length of sequence; the second line contains n numbers, a1, ..., an.

Sum of all n <= 106.
All numbers in the input are positive integers no larger than 109.
 
Output
Output contains T lines.
Each line contains one number, the answer to the corresponding test case.
Since the answers can be very large, you should output them modulo 109+7.
Sample Input
131 2 3
Sample Output
14

 

 

【题目分析】

[此题题意]  是给一个数组:a1,a2,...an,每个元素有1-ai这前i种颜色,然后每种元素一次都可以选择自身允许的一种颜色,组成一个序列,这个序列上颜色相同的组成一个颜色

块,每次统计一个序列上的颜色块数,问具有最多颜色块的那个序列的块数之和。

[初见题目]   拿到这题,首先第一感觉是这题好动态!动态的原因有两点:

一是首先不知道序列怎么排才能得到最大的块数;

二是不知道怎么去统计块数,没有个具体规则,也没有顺序;

[解题探索]  首先想的当然是用动态规划去做,但是首先找不到序列的排列方向,更无法去判断无后效性等,所以当时觉得动态做不了(后来才知道动态也可以做,只能说自己的

动归水平还不够);

                   于是想能否先贪心把序列排出来啊,按升序或降序排列什么的,但是光是样例给出的最大排列就不是升序或降序,至此贪心的路也终止了;

                   动态问题除了贪心和动规外,真不知道还能用什么做了,最无奈的办法'搜索'这题也肯定不行,连个序都没有怎么搜啊,况且这数据规模搜索肯定超时,然后暂时就没有然后了;

                  后来得到别人一点启示,不是这题解法的启示,而是对于 ”解决动态问题的高效解法“的启示,那就是这类大数据高度动态的题最高效最直接的解法莫过于找规律,推

导出公式或是找出周期之类的,这是最朴素,最直接,却也是最高效的解法,虽然只适用于部分题,不过当想不到其他思路时,这也提供了又一个思路。

                  于是开始老老实实的一点点模拟题目的运算过程,紧紧抓住块数这一出发点,想法和题解有点相似,在纸上列出n个序列,每个序列下a1*a2*,...,an个元素,则第一行

贡献块数就为a1*a2*,...,an个,第二行总数一定,主要统计它的和前一个颜色相同的个数,只要从总数里减去就可以了,于是这下通过列实例情况查找,发现了原来第i+1行和第

i 行重复的个数是[Min(ai,ai+1)/(ai*ai+1)]* (a1*a2*...an),然后又通过列举发现[Min(ai,ai+1)/(ai*ai+1)]可写成1/Max(ai,aj) ;这下就是求(a1*a2*...an)[n-sum(1/Max

(ai,aj) );要这个式子最大只需要sum(1/Max(ai,ai+1))尽量小,也就是尽量取ai,ai+1大的,于是我就按照这种有些贪心的思想去排序列,尽量把大的元素利用到,就尽量把大的

元素插在两个较小的元素的中间,结果发现是总是可以将前n/2大的元素按这样排好的,这样就把序列确定下来了,只要取前n/2的元素,分下奇偶情况,sum(1/Max(ai,ai+1))

就直接计算出来了,这不明显就是公式吗!这真是柳暗花明又一村。。。

 

【常见错误总结】:

      1.细节错误:sum=(sum+(2*arr1[n-i]*arr2[n-i+2]))%MOD;不知不觉中写成了sum=(2*(sum+arr1[n-i]*arr2[n-i+2]))%MOD;

      2.重复错误纠正不完全:错误1中有两处,找了半天好不容易找到其中之一改了过来,殊不知第二竟然没改!         

【源程序】

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;#define MOD 1000000007#define Max(a,b) (a)>(b)?(a):(b)#define Min(a,b) (a)<(b)?(a):(b)__int64 arr[1000005],arr1[1000005],arr2[1000005];int main(){//freopen("iofile\\input1.txt","r",stdin);__int64 T,n,i,sum;scanf("%I64d",&T);//cin>>T;while(T--){scanf("%I64d",&n);//cin>>n;for(i=1;i<=n;i++)scanf("%I64d",&arr[i]);//cin>>arr[i];sort(arr+1,arr+n+1);arr1[0]=1;arr2[n+1]=1;for(i=1;i<=n;i++){arr1[i]=(arr1[i-1]*arr[i])%MOD;//mul=(mul*arr[i])%MOD;arr2[n+1-i]=((arr2[n+2-i])*arr[n+1-i])%MOD;}sum=0;for(i=1;i<n/2;i++)sum=(sum+(2*arr1[n-i]*arr2[n-i+2]))%MOD;if(n%2==1)sum=(sum+2*(arr1[(n+1)/2]*arr2[(n+5)/2]))%MOD;elsesum=(sum+arr1[n/2]*arr2[(n+4)/2])%MOD;__int64 ans=(((n*arr1[n])%MOD-sum)%MOD+MOD)%MOD;if(n==1)    printf("%I64d\n",arr[n]%MOD);else    printf("%I64d\n",ans);}return 0;}


 

原创粉丝点击