题目翻译
题解
首先很容易想到,我们只需要算“二维前缀和”,即以某点为右下角的子矩阵内的元素和。然后要求的那个值用容斥原理减一减就能算出来。
下面看怎么算二维前缀和(当然是模意义下的),记为S(i,j):
以6*6矩阵为例:
假设我们想算S(2,5)。它由两部分组成:第一部分是左上角的2*2矩阵,也就是1+2+3+4,这是一个自然数列求和。第二部分是三列:5,6;10,11;和17,18。特点是什么呢?我们来看每一列的和,也就是5+6,10+11,17+18这三项。第一项5+6是等差数列求和。然后相邻两项之差,第一次是(10+11)-(5+6)=2*5,第二次是(17+18)-(10+11)=2*(5+2),如果继续算的话,还有(26+27)-(17+18)=2*(5+2+2)……也就是说,相邻两项的差每次增加2*2(第一个2是x,因为x行的规律相同)。
可以发现,我们只需要实现两个函数:①等差数列求和(一阶级数求和),②二阶级数求和。二阶级数的意义就是说,其差分序列是一个等差数列(相邻元素的差匀速增大)。
先不考虑我们怎么实现一阶/二阶级数求和。有一个现实的问题:题目要求输出后十位,那么我们可以对10^10取模。但怎么判断答案是否小于10^10呢?
方法很tricky:先对10^10取模算一下答案,再对10^10+9取模算一下答案。如果答案相同,就认为答案小于10^10.
那么,具体怎么实现两个级数求和呢?
我先是看着数据组数T<=10^5不大,所以写了个二分快速乘+矩阵乘法(二分快速乘是因为10^10的平方会爆long long),然后光荣TLE……
然后想着既然矩阵乘法会T那就推公式。等差数列的公式谁都会吧……二阶级数的公式可以展开后利用公式:1^2 + 2^2 + … + n^2 = n(n+1)(2n+1) / 6.
注意,不能用逆元做除法(10^10非质数)。除法怎么办呢?二阶级数的结果中有类似(n-2)(n-1)n/6这样的式子,显然n-2,n-1,n三项都不会爆long long,那我们把这三个数写出来,看谁是2或3的倍数,直接除掉,最后乘起来……许多乘法都需要用二分快速乘,尤其是等差数列中项数最大可能是(10^9)^2=10^18,不注意这一点会WA。
然后光荣地又T了。没错,对于T=10^5,O(TlogN)的算法TLE了……
然后把二分快速乘改为常数快速乘:
return ((x*y-(LL)(((long double)x*y+0.5)/MOD)*MOD)%MOD+MOD)%MOD;
就过了。
(doge脸)人类为何要互相伤害……卡常数的意义是什么……
代码
#include#include #include #include using namespace std; #define sqr(x) ((x)*(x)) typedef long long LL; LL MOD; inline LL realmod(LL x){ x%=MOD; if(x<0) x+=MOD; return x; } inline LL quickmul(LL x,LL y){ x=realmod(x),y=realmod(y); return ((x*y-(LL)(((long double)x*y+0.5)/MOD)*MOD)%MOD+MOD)%MOD; } inline LL series_1(LL a,LL b,LL n){//一阶级数 //首项为a,公差为b,共n项 a=realmod(a),b=realmod(b); LL ans=quickmul(a,n); static LL c[2]; c[0]=n-1,c[1]=n; for(int i=0;i<2;i++){if(c[i]%2==0){c[i]/=2;break;}} ans+=quickmul(b,quickmul(c[0],c[1])); return realmod(ans); } inline LL series_2(LL a2,LL a1,LL b,LL n){//二阶级数 //首项为a2,第一次加a1,然后每次多加b,共n项 a2=realmod(a2),a1=realmod(a1),b=realmod(b); LL ans=series_1(a2,a1,n); static LL c[3]; c[0]=n-2,c[1]=n-1,c[2]=n; for(int i=0;i<3;i++){if(c[i]%2==0){c[i]/=2;break;}} for(int i=0;i<3;i++){if(c[i]%3==0){c[i]/=3;break;}} ans+=quickmul(b,quickmul(c[0],quickmul(c[1],c[2]))); ans=realmod(ans); return realmod(ans); } inline LL calc(LL x,LL y){ if(x<1||y<1) return 0; LL ans; ans=series_1(1,1,sqr(min(x,y))); if(x>y){ LL a2=series_1((y+1)*(y+1),-1,y); LL a1=realmod((2*y+3)*y); LL b=realmod(2*y); ans+=series_2(a2,a1,b,x-y); } else if(y>x){ LL a2=series_1(x*x+1,1,x); LL a1=realmod((2*x+1)*x); LL b=realmod(2*x); ans+=series_2(a2,a1,b,y-x); } return ans; } inline LL calc(LL x1,LL y1,LL x2,LL y2){ return realmod(calc(x2,y2)-calc(x2,y1-1)-calc(x1-1,y2)+calc(x1-1,y1-1)); } LL mod1=1e10,mod2=1e10+9; inline void work(void){ int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); MOD=mod1; LL res1=calc(x1,y1,x2,y2); MOD=mod2; LL res2=calc(x1,y1,x2,y2); if(res1!=res2){ printf("..."); printf("%.10lld\n",res1); } else printf("%lld\n",res1); } int main(){ freopen("endlessmatrix.in","r",stdin); freopen("endlessmatrix.out","w",stdout); int T; scanf("%d",&T); while(T--) work(); return 0; }