URAL 1560

题目大意:给出数组n个元素的数组a1,a2,...,an,对数组进行m个两种操作,I i d,给ai+d,C L R k,求从aL到aR这段序列对应的初等对称多项式:S0,...,Sk对p取模。

Time Limit:4000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u

数据规模:1<=n<=80000,abs(ai)<=10^5,1<=m<=100000,1000<=p<=10^9(素数)。1<=L<=R<=n,k<=R-L+1。1<=i<=n,abs(d)<=10^5。

理论基础:初等对称多项式的定义:参见链接1。

          多项式乘法展开式的规律:这个属于高中的排列组合知识了,想必您一定比我熟悉。

          树状数组或者线段树,请自学。

题目分析:根据初等对称多项式的定义,我们可以发现:

S0=1,S1(L->R)=aL+a2+...+aR。那么,S2呢,S3,S4呢?我们记t1=aL+a2+...+aR,ti=aL^i+a2^i+...+aR^i。

那么再看一遍:

S1=t1;

S2=(t1^2-t2)/2;

S3=(t1^3-3*t1*t2+2*t3)/6;

S4=(t1^4-6*t1*t1*t2+8*t1*t3+3*t2*t2-6*t4)/24;

至于证明呢,如果您组合数学得好,可以用组合数展开整明吗,如果不熟悉也没关系,要明白,凡是定理对特例都成立,所以要推Sk,你就把:(a1+a2+a3+a4)^k展开就好啦,然后在展开项里凑t1,t2,t3,t4。

讲到这里,我们就把题目变成了,反复修改单值,反复查询区间和的问题了。对于这类问题,自然想到树状数组了。

这样,大体算法就算完成了,别急,还有几点问题。

1.如果Sk就按照公式那样表示的话,那么t1,t2,t3,t4就必须保持原值,否则可能前面的式子不能被2,6或者24整除。

2.(warning)如果:a^2=m(mod p),d^2=n(mod p),(a+d)^2(mod p)一般不等于m+n(mod p),怎么解决?

3.要求输出正整数,a%p不一定是整数哦。

不要害怕,有问题我们一个个解决。

1.根据同余的性质我们大可不必除以2,6,24,相反我们可以给原式乘以2,6,24的逆元。

   根据费马小定理:a^(p-1)=1(mod p),gcd(a,p)=1,p为奇素数。那么:2,6,24的逆元不就是:2^(p-2),6^(p-2),24^(p-2)?这个问题不就解决了?

2.我们可以将一次改值转换为两次改值,即先加上:-ai^k,这样这个位置就为0了,然后:我们再+(d+ai)^k,不就可以了?

3.我们定义一个取模函数,int mod(LL a,int b){return (a%b+b)%b; }不就行了。

再次提醒:时刻谨防溢出问题。

代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef double db;
#define DBG 0
#define sl(c) ((int)(c).size())    //取字符串长度;
#define forl(i, a, b) for(int i = (a); i <  (b); ++i)    //不带边界值循环,正序
#define forle(i, a, b) for(int i = (a); i <= (b); ++i)   //带边界值循环,正序
#define forh(i, a, b) for(int i = (a); i >  (b); --i)     //不带边界值,逆序
#define forhe(i, a, b) for(int i = (a); i >= (b); --i)        //带边界值,逆序
#define forlc(i, a, b) for(int i##_b = (b), i = (a); i <  i##_b; ++i)  //带别名的循环,用于不可改变值
#define forlec(i, a, b) for(int i##_b = (b), i = (a); i <= i##_b; ++i)
#define forhc(i, a, b) for(int i##_b = (b), i = (a); i >  i##_b; --i)
#define forhec(i, a, b) for(int i##_b = (b), i = (a); i >= i##_b; --i)
#define forall(i, v   )  forl(i, 0, sz(v))   //循环所有
#define forallc(i, v   ) forlc(i, 0, sz(v))
#define forlla(i, v   ) forhe(i, sz(v)-1, 0)
#define forls(i, n, a, b) for(int i = a; i != b; i = n[i])   //搜表用
#define rep(n)  for(int               repp = 0; repp <    (n); ++repp)
#define repc(n) for(int repp_b = (n), repp = 0; repp < repp_b; ++repp)
#define rst(a, v) memset(a, v, sizeof a)   //把字符v填充到a  reset 重置
#define cpy(a, b) memcpy(a, b, sizeof a)   //copy b 的sizeof(a)个字符到a
#define rstn(a, v, n) memset(a, v, (n)*sizeof((a)[0]))  //把字符v填充到a[n]之前的字节
#define cpyn(a, b, n) memcpy(a, b, (n)*sizeof((a)[0]))    //copy b 的 n 个字符到a
#define ast(b) if(DBG && !(b)) { printf("%d!!|\n", __LINE__); while(1) getchar(); }  //调试
#define dout DBG && cout << __LINE__ << ">>| "
#define pr(x) #x"=" << (x) << " | "
#define mk(x) DBG && cout << __LINE__ << "**| "#x << endl
#define pra(arr, a, b)  if(DBG) {\
    dout<<#arr"[] |" <<endl; \
    forlec(i, a, b) cout<<"["<<i<<"]="<<arr[i]<<" |"<<((i-(a)+1)%8?" ":"\n"); \
    if((b-a+1)%8) puts("");\
}                                                             //数列查看
#define rd(type, x) type x; cin >> x   //读数
inline int     rdi() { int d; scanf("%d", &d); return d; }
inline char    rdc() { scanf(" "); return getchar(); }
inline db rddb() { db d; scanf("%lf", &d); return d; }
template<class T> inline bool updateMin(T& a, T b) { return a>b? a=b, true: false; }
template<class T> inline bool updateMax(T& a, T b) { return a<b? a=b, true: false; }
typedef long long LL;
typedef long long unsigned int LU;
#define N 80000
int lowbit[N+1],c1[N+1],c2[N+1],c3[N+1],c4[N+1],A[N+1],n,m,k;
LL a,d,p;
inline int mod(LL a,int b)
{
    if(a>=0)return a%b;
    return (a%b+b)%b;
}
#define Setlb(lowbit,n) forlec(i,1,n) { lowbit[i]=(i&(-i)); }    //设置lowbit[i];
LL getsum(int *c,LL x) { LL result = 0;while(x > 0) { result = (result+c[x])%p; x -= lowbit[x]; } return result; }
void Modify(int *c,int i, int n,int delta)
{
    for (int x = i; x<= n; x += lowbit[x]) c[x] = (c[x]+delta)%p;
}
LL pow_mod(LL a,int b,int p)
{
    LL res=1;
    while(b>0)
    {
        if(b&1)res=(res*a)%p;
        a=(a*a)%p;
        b>>=1;
    }
    return res;
}
int main()
{
    char c;
    scanf("%d%d%I64d",&n,&m,&p);
    Setlb(lowbit,n);
    pra(lowbit,1,n)
    forle(i,1,n)
    {
        scanf("%I64d",&a);
        A[i]=mod(a,p);
        Modify(c1,i,n,(a%p+p)%p);
        Modify(c2,i,n,(a*a)%p);
        Modify(c3,i,n,mod(a*a*a,p));
        Modify(c4,i,n,(((a*a)%p)*((a*a)%p))%p);
    }
    pra(c1,1,n)
    pra(c2,1,n)
    pra(c3,1,n)
    pra(c4,1,n)
    pra(A,1,n)
    LL k1,k2,k3;
    k1=pow_mod(2,p-2,p);
    k2=pow_mod(6,p-2,p);
    k3=pow_mod(24,p-2,p);
    dout<<pr(k1)<<endl\
    <<pr(k2)<<endl\
    <<pr(k3)<<endl;
    while(m--)
    {
        getchar();
        scanf("%c",&c);
        dout<<pr(c)<<endl;
        if(c=='I')
        {
            int i;
            scanf("%d%I64d",&i,&d);
            LL t1=((A[i]+d)%p+p)%p,t2=A[i];
            Modify(c1,i,n,(d%p+p)%p);
            Modify(c2,i,n,mod(-t2*t2,p));
            Modify(c2,i,n,t1*t1%p);
            Modify(c3,i,n,mod(-t2*t2%p*t2%p,p));
            Modify(c3,i,n,mod(t1*t1%p*t1,p));
            Modify(c4,i,n,mod(-((t2*t2)%p)*((t2*t2)%p),p));
            Modify(c4,i,n,mod(((t1*t1)%p)*((t1*t1)%p),p));
            A[i]=t1;
            pra(c1,1,n)
            pra(c2,1,n)
            pra(c3,1,n)
            pra(c4,1,n)
            pra(A,1,n)
            continue;
        }
        LL t1,t2,t3,t4,L,R;
        scanf("%I64d%I64d%d",&L,&R,&k);
        switch(k)
        {
            case 4:
            {
                t1=mod(getsum(c1,R)-getsum(c1,L-1),p);
                t2=mod(getsum(c2,R)-getsum(c2,L-1),p);
                t3=mod(getsum(c3,R)-getsum(c3,L-1),p);
                t4=mod(getsum(c4,R)-getsum(c4,L-1),p);
                LL s1,s2,s3,s4;
                s1=t1;
                s2=((t1*t1%p-t2)%p)*k1;
                s3=((((t1*t1)%p*t1)%p
                    -(3*t1%p*t2)%p
                    +2*t3%p))%p*k2;
                s4=((((((t1*t1)%p)*((t1*t1)%p))%p)
                     -(6*t2%p*((t1*t1)%p))%p
                     +(8*(t1*t3%p))%p
                     +3*t2*t2%p
                     -6*t4%p))%p*k3;
                printf("1 %d %d %d %d\n",mod(s1,p),mod(s2,p),mod(s3,p),mod(s4,p));
                break;
            }
            case 3:
            {
                t1=mod(getsum(c1,R)-getsum(c1,L-1),p);
                t2=mod(getsum(c2,R)-getsum(c2,L-1),p);
                t3=mod(getsum(c3,R)-getsum(c3,L-1),p);
                LL s1,s2,s3;
                s1=t1;
                s2=((t1*t1%p-t2)%p)*k1;
                s3=((((t1*t1)%p*t1)%p
                    -(3*t1%p*t2)%p
                    +2*t3%p))%p*k2;
                printf("1 %d %d %d\n",mod(s1,p),mod(s2,p),mod(s3,p));
                break;
            }
            case 2:
            {
                t1=mod(getsum(c1,R)-getsum(c1,L-1),p);
                t2=mod(getsum(c2,R)-getsum(c2,L-1),p);
                LL s1,s2;
                s1=t1;
                s2=((t1*t1%p-t2)%p)*k1;
                printf("1 %d %d\n",mod(s1,p),mod(s2,p));
                break;
            }
            case 1:
            {
                t1=getsum(c1,R)-getsum(c1,L-1);
                printf("1 %d\n",mod(t1,p));
            }
        }
    }
    return 0;
}
    

其中,pra()是调试用的,DBG为0,所以可以不用理会。

参考文献:

http://zh.wikipedia.org/wiki/%E5%B0%8D%E7%A8%B1%E5%A4%9A%E9%A0%85%E5%BC%8F

by:Jsun_moon http://blog.csdn.net/Jsun_moon

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值