题目大意:给出数组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