Codeforces #278

来源:互联网 发布:淘宝怎么做定制玻璃 编辑:程序博客网 时间:2024/06/03 21:05

D. 给一个数组,求最少把这个数组分成多少部分,要求每一部分都大于等于l,且最大值和最小值的差不大于s。

显然是dp的思想,大概就是dp[i]=min{dp[k]}+1(j<=k<=i-l),j表示最远从哪个数开始到第i个数满足j到i中最大值和最小值的差不大于s,这个j可以用单调队列解决,开两个单调队列,一个维护区间最大一个维护区间最小。知道j之后,就要从j到i-l之间找到一个dp最小的值,用线段树可以搞。

代码:

#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;#define N 100005#define INF 99999999struct Tree{    int l,r,val;};Tree tree[N*5];int incQueue[N],incHead,incTail;int decQueue[N],decHead,decTail;int dpQueue[N],dpHead,dpTail;int a[100005];int dp[100005];void build(int t,int l,int r){    tree[t].l=l;    tree[t].r=r;    tree[t].val=INF;    if (l==r) return;    int mid=(l+r)/2;    build(2*t+1,l,mid);    build(2*t+2,mid+1,r);}void add(int t,int idx,int val){    if (tree[t].l==tree[t].r)    {        tree[t].val=val;        return;    }    int mid=(tree[t].l+tree[t].r)/2;    if (idx<=mid) add(2*t+1,idx,val);    else add(2*t+2,idx,val);    tree[t].val=min(tree[2*t+1].val,tree[2*t+2].val);}int query(int t,int l,int r){    if (tree[t].l==l && tree[t].r==r)    {        return tree[t].val;    }    int mid=(tree[t].l+tree[t].r)/2;    int ret=INF;    if (l<=mid) ret=min(ret,query(2*t+1,l,min(mid,r)));    if (r>mid) ret=min(ret,query(2*t+2,max(l,mid+1),r));    return ret;}void pushInc(int idx){    while(1)    {        if (incTail==incHead) break;        if (a[incQueue[incTail-1]]<=a[idx]) break;        incTail--;    }    incQueue[incTail++]=idx;}void pushDec(int idx){    while(1)    {        if (decTail==decHead) break;        if (a[decQueue[decTail-1]]>=a[idx]) break;        decTail--;    }    decQueue[decTail++]=idx;}int main(){    int i,j,n,s,l;    scanf("%d%d%d",&n,&s,&l);    for (i=0;i<n;i++)    {        scanf("%d",&a[i]);    }    incHead=incTail=decHead=decTail=dpHead=dpTail=0;    build(0,0,n-1);    int far=0;    for (i=0;i<n;i++)    {        pushInc(i);        pushDec(i);        while(1)        {            if (a[i]-a[incQueue[incHead]]>s)            {                far=max(far,incQueue[incHead]+1);                incHead++;            }            else break;        }        while(1)        {            if (a[decQueue[decHead]]-a[i]>s)            {                far=max(far,decQueue[decHead]+1);                decHead++;            }            else break;        }        if (i-far+1<l) dp[i]=-1;        else        {    //        printf("%d:%d!\n",i,far);            if (far==0)            {                dp[i]=1;                add(0,i,dp[i]);            }            else            {                int tmp=query(0,far-1,i-l);            //    printf("%d: %d~%d %d\n",i,far-1,i-l,tmp);                if (tmp==INF) dp[i]=-1;                else                {                    dp[i]=tmp+1;                    add(0,i,dp[i]);                }            }        }    }  //  for (i=0;i<n;i++) printf("%d ",query(0,i,i));  //  printf("\n");    if (dp[n-1]==INF) printf("-1\n");    else printf("%d\n",dp[n-1]);    return 0;}


E. 给n,求一个n的全排列A使得A1*A2*...*A_i%n(1<=i<=n)这n个数为0到n-1的一个全排列。

只有质数和1和4有解,因为对于合数N可以有N=p1*p2,而p1和p2可以分别从小于N的数当中凑得,因此无论怎么排都会有大于1个取模的结果为0。

4有解是因为4=2*2,而1~3当中只有一个2因子,所以可能会存在解,事实证明也是有解的(1, 3, 2, 4)

因为想要让这个全排列的前i个数相乘模n的结果都不一样,因此n要放在最后一位(不然乘以n之后无论怎么乘 模都是0),1要放在第一位(乘以1之后模不变)。

之后当然看看可不可以构造一个序列使得:

A1%n=1

A1*A2%n=2

A1*A2*A3%n=3

...

A1*A2*...*A_(n-1)%n=n-1

A1*A2*...*An%n=0

且A1=1, An=n。

如果这种构造方法成立,就要证明不存在某个数p(1<p<N),使得ip%n=i+1且jp%n=j+1,根据这两个式子可得:

(i+1+xn)/i=(j+1+yn)/j => 

j+xnj=i+yni  =>

i-j=n(xj-yi)

因为i和j都在0和n之间,因此i-j会小于n,但是右边xj-yi是整数,所以唯一满足上述式子的情况就是左边等于右边等于0,即i==j。

所以就证明了不存在某个数p,使得ip%n=i+1且jp%n=j+1。

也就是说这种构造方法是满足A1到An互不相等的。


接下来需要考虑怎么快速的得到p,使得(i-1)*p mod n = i。

考虑费马小定理:a^(n-1) mod n = 1

因此,我们可以让p等于(i-1)^(n-2) * i

这样(i-1)*p=  (i-1) * (i-1)^(n-2) * i = (i-1)^(n-1)*i

这样(i-1)^(n-1) mod n=1,所以(i-1)^(n-1)*i mod n = i,即(i-1) * (i-1)^(n-2)*i mod n = i

所以每次只需要快速幂求出这个p,打印输出即可。

代码很简单

#include <stdio.h>#include <math.h>#include <algorithm>using namespace std;bool is_prime(int t){    for (int i=2;i<=sqrt(t*1.0);i++)    {        if (t%i==0) return false;    }    return true;}long long f_pow(long long x,int n,int mod){    long long ret=1;    while(n)    {        if (n&1) ret=ret*x%mod;        n>>=1;        x=x*x%mod;    }    return ret;}int main(){    int i,j,n;    scanf("%d",&n);    if (!is_prime(n) && n!=1 && n!=4)    {        printf("NO\n");        return 0;    }    printf("YES\n");    if (n==1)    {        printf("1\n");        return 0;    }    if (n==4)    {        printf("1\n3\n2\n4\n");        return 0;    }    printf("1\n");    for (i=2;i<n;i++)    {        printf("%d\n",f_pow(i-1,n-2,n)*i%n);    }    printf("%d\n",n);    return 0;}


0 0
原创粉丝点击