bzoj4815[CQOI2017]小Q的格子
##题意
不简述题意了,简述题意之后这道题就做出来了.放个原题面.
小Q是个程序员.
作为一个年轻的程序员,小Q总是被老C欺负,老C经常把一些麻烦的任务交给小Q来处理.
每当小Q不知道如何解决时,就只好向你求助.
为了完成任务,小Q需要列一个表格,表格有无穷多行,无穷多列,行和列都从1开始标号.
为了完成任务,表格里面每个格子都填了一个整数.
为了方便描述,小Q把第a行第b列的整数记为f(a,b).
为了完成任务,这个表格要满足一些条件:
(1)对任意的正整数a,b,都要满足f(a,b)=f(b,a)
(2)对任意的正整数a,b,都要满足b*f(a,a+b)=(a+b)*f(a,b)
为了完成任务,一开始表格里面的数很有规律,第a行第b列的数恰好等于a*b,显然一开始
是满足上述两个条件的.
为了完成任务,小Q需要不断的修改表格里面的数,每当修改了一个格子的数之后,为了让
表格继续满足上述两个条件,小Q还需要把这次修改能够波及到的全部格子里都改为恰当
的数.
由于某种神奇的力量驱使,已经确保了每一轮修改之后所有格子里的数仍然都是整数.
为了完成任务,小Q还需要随时获取前k行前k列这个有限区域内所有数的和是多少,答案可
能比较大,只需要算出答案mod1,000,000,007之后的结果。
数据范围:100%的数据1<=m<=10^4,1<=a,b,k<=n<=4*10^6,0<=x<10^18
分析
一句话题解:这道题是数论题
n<=4*106,无法直接存储n2大小的矩阵.因此我们猜测这道题有一些优雅的性质使得我们可以较为简单地求和.
给出的两个条件相当简单,简单到让人乍一看无处下手.要做出这道题,就要先分析这两个条件
首先我们需要仔细考虑"这次修改能够涉及到的全部格子"是什么意思.
也就是说,到底符合什么性质的格子之间会有影响.
"两个格子互相影响"的含义是修改一个格子会导致另一个格子被修改.
用<=>这个符号表示"互相影响"
根据条件(1),(a,b)<=>(b,a)
根据条件(2),(a,b)<=>(a,a+b)
结合(1)(2), (a,b)<=>(a+b,a)
令c=a+b,那么(a,b)<=>(a,a+b)可以写作(a,c-a)<=>(a,c)
也就是说,(a,b)<=>(a,a-b),此处不妨令a>b
对两个条件做了这么一些分析后,我们发现,把横坐标加上/减去纵坐标,或者把纵坐标加上/减去横坐标,
得到的格子和原先的格子互相影响.
如果熟练掌握了辗转相减法,我们就知道两个简单的性质:gcd(x,y)=gcd(x,x+y)=gcd(x,x-y)
也就是说,对于两个格子A(xa,ya),B(xb,yb),如果A<=>B,那么gcd(xa,ya)=gcd(xb,yb)
换一种说法,两个格子相互影响,当且仅当第一个格子横纵坐标的gcd=第二个格子横纵坐标的gcd.
也可以说,横纵坐标的gcd=1的所有格子之间互相影响,gcd=2的所有格子互相影响,gcd=3的格子...
我们还没有分析透这两个条件,因为我们没有利用第二个条件中的定量关系.
将条件(2)移项,得到f(a,a+b)/(a+b)=f(a,b)/b
也就是说,横坐标相同的格子,如果之间有影响,数值和纵坐标成正比.
同理,纵坐标相同的格子,如果之间有影响,数值和横坐标成正比.
横纵坐标都不相同的格子怎么办?注意如果这两个格子相互影响,我们总可以通过多次在同一行内或同一列内的变换从一个格子"走到"另一个格子,每一步都满足数值与横/纵坐标成正比,那么这两个格子的数值与它们横纵坐标的乘积成正比.也就是说:如果gcd(a,b)=gcd(x,y),那么f(a,b)/(a*b)=f(x,y)/(x*y)
再仔细考虑.在相互影响的一些格子里,必然存在一个数值最小的.如果这些格子横纵坐标的gcd均为g,那么f(g,g)是这些格子中数值最小的一个,且其他格子的数值都可以表示为f(g,g)的倍数.
这就启发了我们的解题思路:所有格子都可以表示为某一个f(g,g)的倍数,那么对于查询操作我们枚举g,求出每一个f(g,g)对答案的贡献.
(也就是说,对于所有横纵坐标的gcd=g的格子(x,y),我们把(x/g)*(y/g)
求和,乘上f(g,g)就得到了f(g,g)对答案的贡献)
于是我们存储所有的f(g,g),这只需要O(n)的空间,解决了n^2大小的矩阵无法直接存储的问题.
对于修改操作,我们容易找到修改的格子(x,y)所影响的那个f(g,g),把f(x,y)的新数值除以对应的系数(x/g)*(y/g)
,就得到了f(g,g)的新数值.
接下来我们需要解决的问题是:快速求解每个f(g,g)对答案的贡献.
注意n,m的乘积可以达到4*10^10
,如果对于每个询问枚举g,即使单个的g可以在O(1)内求解,时间上也无法承受.因此到现在我们可以认定这道题是一道数论题
要统计的式子是:
int ans=0;
for(g=1;g<=k;++g)
for(i=1;i<=k;++i)
for(j=1;j<=k;++j)
if(gcd(i,j)==g)ans+=f(g,g)*(i/g)*(j/g);
return ans;
//pseudocode,don't care about module or overflow
震惊!辣鸡博主连求和符号都不会打还来写博客骗访问量!
根据gcd计数的套路,因为是1<=i<=k,1<=j<=k,两维的大小均为k,我们考虑利用欧拉函数进行计数.但这个题里欧拉函数出现的形式并不显然.
不妨先考虑问题的简化版本:
int ans=0;
for(g=1;g<=k;++g)
for(i=1;i<=k;++i)
for(j=1;j<=k;++j)
if(gcd(i,j)==g)ans+=f(g,g);
return ans;
//pseudocode,don't care about module or overflow
这个形式里,f(g,g)在答案中贡献了多少个f(g,g)是一目了然的:就是1<=i<=k,1<=j<=k且gcd(i,j)=g的(i,j)对数
.莫比乌斯反演可以推导出来,但本题用欧拉函数更为简便.
i和j必然都是g的倍数.那么从1到k,g的倍数有[k/g]个,因此(i,j)可以表示为(p*g,q*g),1<=p,q<=[k/g]
.如果满足条件,需要gcd(p,q)=1
.
也就是说,问题相当于1<=p<=[k/g],1<=q<=[k/j]时,gcd(p,q)=1的对数.
因为p=q的时候只有(1,1)是合法的,我们可以先考虑p<q的时候有多少对(p,q),根据对称性,答案乘2就是p!=q时的对数,再加上p=q时的一个就好了.
而对于一个确定的q,p<q的时候合法的(p,q)对数相当于小于q且和q互质的数字个数.这应当都会算了.
于是对于一个确定的g,答案等于2*(phi[1]+phi[2]+phi[3]+...+phi[k/g])-1
,这个式子里我们把phi[1]放在括号里一起乘2,那么p=q时(1,1)被算了两次,所以要减一.
根据数论里常用的按除法结果分块
的技巧,不同的[k/g]的取值只有sqrt(k)个,枚举这sqrt(k)个取值,统计时乘上f(g,g)的区间和f(i,i)+f(i+1,i+1)+...+f(j-1,j-1)+f(j,j)
,比较显然的树状数组维护前缀和将需要O(msqrt(n)log(n))
的时间做查询.注意到修改操作比查询次数少到不知道哪里去了,我们可以利用分块,牺牲修改的复杂度,实现O(sqrt(n))修改,O(1)查询
.这个分块的细节看代码即可.还需要线性筛预处理欧拉函数前缀和,总的时间复杂度为O(n+m*sqrt(n))
那么我们来考虑另一个简化版本.
int ans=0;
for(g=1;g<=k;++g)
for(i=1;i<=k;++i)
for(j=1;j<=k;++j)
if(gcd(i,j)==g)ans+=f(g,g)*(i/g);
return ans;
//pseudocode,don't care about module or overflow
这里我们乘上了之前两个系数中的一个.按照和上一个简化版本相同的推导思路,(i,j)可以表示为(p*g,q*g),1<=p,q<=[k/g]
.如果满足条件,需要gcd(p,q)=1
.
也就是说,问题相当于1<=p<=[k/g],1<=q<=[k/j]时,gcd(p,q)=1的所有p之和
.因为p=q的时候只有(1,1)是合法的,我们可以先考虑p<q
的时候所有(p,q)的贡献之和,根据对称性,答案乘2就是p!=q时的贡献之和,再加上p=q时的1就好了.
而对于一个确定的q,p<q的时候合法的(p,q)的p之和相当于小于q且和q互质的数字之和.这个东西在q!=1的时候等于q*phi[q]/2
乘2之后,我们要算的是phi[1]*1+phi[2]*2+phi[3]*3+...+phi[k/g]*(k/g)
,这里(1,1)恰好只算了一次.
那么这个东西也可以预处理出前缀和,其他做法和上一个简化版本相同.
考虑原始问题.
int ans=0;
for(g=1;g<=k;++g)
for(i=1;i<=k;++i)
for(j=1;j<=k;++j)
if(gcd(i,j)==g)ans+=f(g,g)*(i/g)*(j/g);
return ans;
//pseudocode,don't care about module or overflow
沿着两个简化版本的思路,不难发现对于一个确定的q,p<q
的(p,q)对答案的贡献之和为小于q且和q互质的数字之和再乘q.这个东西在q!=1的时候等于q*q*phi[q]/2
,自然我们预处理i*i*phi[i]
的前缀和即可.其他做法和两个简化版本相同.细节详见代码.
#include<cstdio>
typedef long long ll;
const int mod=1000000007;
const int maxn=4000005;
int phi[maxn],prime[maxn],tot;
bool flag[maxn];
void linear(){
phi[1]=1;
for(int i=2;i<maxn;++i){
if(!flag[i]){
phi[i]=i-1;prime[++tot]=i;
}
for(int j=1;j<=tot&&prime[j]*i<maxn;++j){
flag[prime[j]*i]=true;
if(i%prime[j]==0){
phi[i*prime[j]]=phi[i]*prime[j];
break;
}else phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
phi[1]=1;
for(int i=2;i<maxn;++i)phi[i]=phi[i]*1ll*i%mod*i%mod;
for(int i=1;i<maxn;++i)phi[i]=(phi[i-1]+phi[i])%mod;
}
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
const int sz=2000;
int fii[maxn],pre[maxn];
int belong[maxn],L[maxn],R[maxn];
int mark[maxn];
int totb;
void add(int x,int w){
int r=R[belong[x]];
for(int i=x;i<=r;++i){
pre[i]=(pre[i]+w)%mod;
}
for(int i=belong[x]+1;i<=totb;++i)mark[i]=(mark[i]+w)%mod;
}
int qpre(int x){
return (pre[x]+mark[belong[x]])%mod;
}
int work(int n){
ll ans=0;
ll cnt;
for(int i=1,last;i<=n;i=last+1){
last=n/(n/i);
cnt=phi[n/i];
ans=(ans+cnt*(qpre(last)-qpre(i-1)+mod)%mod);
}
return ans%mod;
}
int main(){
int m,n;scanf("%d%d",&m,&n);
linear();
int a,b,k;ll x;
for(int i=1;i<=n;++i)fii[i]=i*1ll*i%mod;
for(int i=1;i<=n;++i)pre[i]=(fii[i]+pre[i-1])%mod;
for(int i=1;i<=n;++i)belong[i]=(i-1)/sz+1;
totb=(n+sz-1)/sz;
for(int i=1;i<=totb;++i){
L[i]=(i-1)*sz+1;R[i]=i*sz;
}
R[totb]=n;
for(int i=1;i<=m;++i){
scanf("%d%d%lld%d",&a,&b,&x,&k);
int g=gcd(a,b);x=x/(a/g)/(b/g);x%=mod;
add(g,(x-fii[g]+mod)%mod);fii[g]=x;
printf("%d\n",work(k));
}
return 0;
}
bzoj4815[CQOI2017]小Q的格子的更多相关文章
- [BZOJ4815][CQOI2017]小Q的表格(莫比乌斯反演)
4815: [Cqoi2017]小Q的表格 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 832 Solved: 342[Submit][Statu ...
- [bzoj4815]: [Cqoi2017]小Q的表格
来自FallDream的博客,未经允许,请勿转载,谢谢. 小Q是个程序员. 作为一个年轻的程序员,小Q总是被老C欺负,老C经常把一些麻烦的任务交给小Q来处理.每当小Q不知道如何解决时,就只好向你求助. ...
- BZOJ4815 [CQOI2017]小Q的表格 【数论 + 分块】
题目链接 BZOJ4815 题解 根据题中的式子,手玩一下发现和\(gcd\)很像 化一下式子: \[ \begin{aligned} bf(a,a + b) &= (a + b)f(a,b) ...
- [BZOJ4815][CQOI2017]小Q的表格 数论+分块
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4815 题目中所给条件中的$(a,a+b)$和$(a,b)$的关系很瞩目. 然后大家都知道$ ...
- 【BZOJ4815】[CQOI2017]小Q的表格(莫比乌斯反演,分块)
[BZOJ4815][CQOI2017]小Q的表格(莫比乌斯反演,分块) 题面 BZOJ 洛谷 题解 神仙题啊. 首先\(f(a,b)=f(b,a)\)告诉我们矩阵只要算一半就好了. 接下来是\(b* ...
- bzoj 4815: [Cqoi2017]小Q的表格 [数论]
4815: [Cqoi2017]小Q的表格 题意: 单点修改,查询前缀正方形和.修改后要求满足条件f(a,b)=f(b,a), b×f(a,a+b)=(a+b)*f(a,b) 一开始sb了认为一次只会 ...
- bzoj 4813: [Cqoi2017]小Q的棋盘 [树形背包dp]
4813: [Cqoi2017]小Q的棋盘 题意: 某poj弱化版?树形背包 据说还可以贪心... #include <iostream> #include <cstdio> ...
- BZOJ_4813_[Cqoi2017]小Q的棋盘_dfs
BZOJ_4813_[Cqoi2017]小Q的棋盘_dfs Description 小Q正在设计一种棋类游戏.在小Q设计的游戏中,棋子可以放在棋盘上的格点中.某些格点之间有连线,棋子只能 在有连线的格 ...
- 洛咕 P3700 [CQOI2017]小Q的表格
洛咕 P3700 [CQOI2017]小Q的表格 神仙题orz 首先推一下给的两个式子中的第二个 \(b\cdot F(a,a+b)=(a+b)\cdot F(a,b)\) 先简单的想,\(F(a,a ...
随机推荐
- bootstrap 内边框样式
css设置: .table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table ...
- day 2 函数的嵌套
1.函数的嵌套 百度百科 2.产品如何运作 3.应用 1)版本1:打印1条线 #1.定义函数 def print_line(): print("-"*50) #2.调用函数 ...
- .net core 使用windows版redis
在项目中为了减少程序占用内存(将结果保存在全局变量里面,会占用内存),要求使用redis.开始了爬坑的过程.o(╥﹏╥)o c#操作redis 基本就这3中情况: ServiceStack.Redis ...
- Openwrt能用的花生壳客户端
http://files.cnblogs.com/mazhiyong/phddns.zip 使用教程可参考官方文档 http://service.oray.com/question/116.html
- K-means算法实现
目录 K-means K-means x = xlsread("D:\MatlabData\西瓜数据集.xlsx"); m = length(x); [Idx,C]=kmeans( ...
- Unity3D之AR开发(二)
上一篇给大家介绍了高通AR的使用,接下来给大家分享一下EasyAR EasyAR引擎简介 EasyAR是做好用的且免费的增强现实(Augmented Reality)引擎,EasyAR为Unity开发 ...
- 4星|《财经》2018年第13期:年轻人大多从大三和大四起开始就从QQ向微信转移
<财经>2018年第13期 总第530期 旬刊 本期主要话题是快递业,其他我感兴趣的重要话题还有:香港9价HPV疫苗断供风波:华盛顿邮报被贝佐斯收购后这几年的变化:北京二中朝阳学校的划片风 ...
- mongodb4简明笔记
就一数据库,掌握基本用法,其他的现学现卖就行了. 所以要把握基本套路. 创建数据库=>使用数据库=>创建集合=>使用集合=>创建文档=>使用文档 1.数据库 mongod ...
- 【Python入门学习】闭包&装饰器&开放封闭原则
1. 介绍闭包 闭包:如果在一个内部函数里,对在外部作用域的变量(不是全局作用域)进行引用,那边内部函数被称为闭包(closure) 例如:如果在一个内部函数里:func2()就是内部函数, 对在外部 ...
- 网络安全攻防----html基础
一.学习html的硬件条件 网页(.Html)是静态的,学习html语言需要选择Chrome浏览器和Sublime3编辑器,选择Chrome浏览器是因为Chrome浏览器兼容性比较好,支持各种各样的插 ...