北邮OJ 884. 16校赛-Average Modulo

来源:互联网 发布:java web 配置log4j2 编辑:程序博客网 时间:2024/05/22 12:11

时间限制 5000 ms 内存限制 65536 KB

题目描述

We define function g on an array as:

g([a0,a1,,an1])=(Σn1l=0al) mod pn

Here, p is a given constant, and n is the length of the array.
You are given p and an array A and a length k.
You need to find the maximum value of g over all contiguous subarrays of A that are of length ≥ k.

输入格式

The first line of input contains T, the number of test cases. Each test case contains two lines. The first line of each test case contains three integers: N, p and k, respectively.
The second line of each test case contains N integers contained in array A.

1T10
1N5104
1Kmin(300,N),1P109
1Ai109

输出格式

For each test case, output one line containing two space separated integers: p and q. If the answer is an integer x, then output x and 1 separated by a space.
Otherwise, output p and q if pq is the answer is in its simplest form.

Explanation of example
In the first test case, when length ≥2, the maximum g value is obtained when you take the entire array, so g is 53.
In the second case, all f values are even. Hence, when we take modulo 2, the answer is 0 for any array.

In the third case, p is 1, and the value is 0 for any array.

输入样例

3
3 100 2
2 1 2
5 2 1
2 10 4 6 8
5 1 2
100 213142 3123 123 321

输出样例

5 3
0 1
0 1


题意为,找出给定序列中的一个满足长度≥k的连续子序列,使得这个子序列的序列和模上p再除以n尽可能的大。
比如a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,找出其中长度≥2的一段连续子序列,使得他们相加之后mod p = tmp,tmp / n= tmp2,使得tmp2尽可能的大。
乍看之下又是一道枚举题,但是n的范围最大是5104,如果从length=k枚举到length=n,O(n2)的复杂度明显就超时了,这时候有两种思路,第一种思路是自己打表出数据,用O(n2)的枚举法测一下答案的连续子序列长度最大是多少,

打表代码如下

#include<bits/stdc++.h>#define K 5using namespace std;int main(){    srand(time(NULL));    freopen("tsx.txt","w",stdout);    int n=50000,k=200,ts=1000,p=1000000000;    printf("%d\n",ts);    while(ts--){        printf("%d %d %d\n",n,p,k);        for(int i=0;i<n;i++)            printf("%d%c",rand()%p+1,i==n-1?'\n':' ');    }    return 0;}

然后用裸的O(n2)的枚举测了几千组自己生成的样例,答案长度最大小于k的1.5倍,比赛时稳妥起见开了k的3倍,时间5s绰绰有余。

第二种思路呢,是用数学思想证明,首先(a+b)%p =(a%p + b%p)%p

令a=Σ2ki=0ai,b=Σ2k+xi=2k+1ai

(a+b)%p2k+x=(a%p+b%p)%p2k+x
a%p2k+x+ b%p2k+x
≤max(a%p2k , b%px )

即当枚举到长度≥2k但≤4k的时候(更长的情况可以递推),设长度为2k+x,一定可以划分为一个2k和x长度的两个连续子序列,它们的值一定可以代替(大于等于)原始2k+x串的值。

解决代码如下

#include<cstdio>#include<cmath>#include<algorithm>#define MA 50050using namespace std;int a[MA],sum[MA],n,k,p;int gcd(int a,int b){return b==0?a:gcd(b,a%b);}int main(){    int i,j,k,len,t;    for(scanf("%d",&t);t--;){        scanf("%d%d%d",&n,&p,&k);        for(i=1;i<=n;i++)            scanf("%d",&a[i]);        for(i=1;i<=n;i++)            sum[i]=(sum[i-1]+a[i])%p;        int up=0,down=1;        int lim=k*3;//根据数学验证,取2k即可        for(len=k;len<=lim;len++){            for(i=0;i+len<=n;i++){                int tmpa=(p+sum[i+len]-sum[i])%p;                int tmpb=len;                double ta=1.0*up/down;                double tb=1.0*tmpa/tmpb;                if(ta<tb){                    up=tmpa;                    down=tmpb;                }            }        }        int gcd_=gcd(up,down);        printf("%d %d\n",up/gcd_,down/gcd_);    }    return 0;}
0 0
原创粉丝点击