【题解】 [EZEC-4]求和
对于百分之十的数据:随便过。
下面推式子:
\]
\]
\]
\]
\]
令\(T=kd:\)
\]
现在的问题在于\(\sum_{i=1}^\frac{n}{T}\sum_{j=1}^\frac{n}{T}[(\frac{T}{k})^T]^{i+j}.\)
- 线性递推
以下是@SOSCHINA大佬的思路:
设\(g(n)=\sum_{i=1}^n\sum_{j=1}^n k^s.\)
枚举\(s=i+j.\)
则有:
\]
\]
\]
第三行就是两行相减。
对第一行的解释:\([2,n+1]\)这里的数,每个数作为\(i+j\)都出现了\(x-1\)次。因为\(i\)可以取遍\([1,x-1].\)后面的那一些,\([n+2,2n]\)会发现\(i\)最大只能到\(n,\)不能再取遍\(x-1\)个值了。此时能取到的应该是\(2n-s+1\)种。
对于\(g(n+1):\)这里是把第一个式子的最后一个值移动到了后面那个式子,方便做差。
这时我们可以在小模数的情况下做到\(O(n*mod\))的预处理。
- 化简形式
令\(x=(\frac{T}{k})^T.\)
则原式为\(\sum_{i=1}^\frac{n}{T}\sum_{j=1}^\frac{n}{T} x^{i+j}.\)
像不像一个多项式。
它就等于\((x+x^2+...x^\frac{n}{T})^2.\)
于是我们可以等比数列求和解出。
剩下的,可以做到\(O(n\log n\log mod)\)处理出整个式子。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1500001;
int mod,TT;
bitset<MAXN+1>vis;
int p[MAXN+1],mu[MAXN+1],T[MAXN+1],cnt,n,Ans;
inline int Mod(long long x){
if(x<0)return x+mod;
if(x>=mod)return x%mod;
return x;
}
inline int add(int x,int y) {return Mod(1ll*x+1ll*y+1ll*mod);}
inline int mul(int x,int y) {return Mod(1ll*x*y);}
inline int qpow(int a,int b) {
if(!b)return 1;
if(a<=1||b==1)return a;
a %= mod;
int res=1;
while(b) {
if(b&1)res=mul(res,a);
a=mul(a,a);
b>>=1;
}
return res;
}
inline int calc(int x,int y){
if(y==1)return x;
if(x==1)return y;
int ans=x;
int inv=qpow((1-x+mod)%mod,mod-2);
int fm=(1-qpow(x,y)+mod)%mod;
ans=mul(ans,mul(fm,inv));
return ans;
}
inline int Calc(int x,int y){int ans=calc(x,y);return mul(ans,ans);}
int main() {
scanf("%d",&TT);
mu[1]=1;
int N=MAXN;
for(register int i=2; i<=N; ++i) {
if(!vis[i])p[++cnt]=i,mu[i]=-1;
for(register int j=1; j<=cnt&&i*p[j]<=N; ++j) {
vis[i*p[j]]=1;
if(i%p[j]==0)break;
mu[i*p[j]]=-mu[i];
}
}
while(TT--) {
scanf("%d%d",&n,&mod);
N=n;Ans=0;
for(register int i=1; i<=N; ++i) {
for(register int j=i,k,x; j<=N; j+=i) {
k=i;if(!mu[k])continue;
x=qpow(j/k,j);
T[j]=add(T[j],mul(mu[k],Calc(x,n/j)));
}
}
for(register int i=1; i<=n; ++i)Ans=add(Ans,T[i]),T[i]=0;
printf("%d\n",Ans);
}
return 0;
}
由于这里是\(5*10^5\)的数据,所以略微卡常,但笔者通过非常不精湛的卡常技术跑到了\(3s\)以内,所以这里的时间限制我开了\(3.2s\).
对等比数列进行精细处理,可以做到\(O(n\log^2n)\)的复杂度。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN=1500000;
int mod,TT;
bitset<MAXN<<1>vis;
int p[MAXN<<1],mu[MAXN<<1],T[MAXN<<1],cnt,n,Ans;
inline int Mod(long long x){
if(x>=mod)return x%mod;
return x;
}
inline int add(int x,int y) {
return Mod(x+y+mod);
}
inline int mul(int x,int y) {
return Mod(1ll*x*y);
}
inline int qpow(int a,int b) {
if(!b)return 1;
if(a<=1||b==1)return a;
a %= mod;
int res=1;
while(b) {
if(b&1)res=mul(res,a);
a=mul(a,a);
b>>=1;
}
return res;
}
inline int calc(int x,int y){
if(y==1)return x;
int res=calc(x,y/2);
res=add(res,mul(res,qpow(x,y/2)));
if(y&1)res=add(res,mul(x,qpow(x,y-1)));
return res;
}
inline int Calc(int x,int y){int ans=calc(x,y);return mul(ans,ans);}
signed main() {
scanf("%lld",&TT);
mu[1]=1;
int N=MAXN;
for(int i=2; i<=N; ++i) {
if(!vis[i])p[++cnt]=i,mu[i]=-1;
for(int j=1; j<=cnt&&i*p[j]<=N; ++j) {
vis[i*p[j]]=1;
if(i%p[j]==0)break;
mu[i*p[j]]=-mu[i];
}
}
while(TT--) {
scanf("%lld%lld",&n,&mod);
N=n;
Ans=0;
for(int i=1; i<=N; ++i) {
for(int j=i; j<=N; j+=i) {
int k=i;
int x=qpow(j/k,j);
if(!mu[k])continue;
T[j]=add(T[j],mul(mu[k],Calc(x,n/j)));
}
}
for(int i=1; i<=n; ++i)Ans=add(Ans,T[i]),T[i]=0;
cout<<Ans<<endl;
}
return 0;
}
由于常数等原因,这分代码可以拿到\(50\)分的好成绩。但我们可以通过另一种做法将常数/复杂度降低。
另一种做法
观察:
\]
\]
这里同样观察式子发现可以直接算。前一部分是\(O(n\ln n)\)的\(n\)倍调和级数的复杂度,后面带上一个\(O(\log n)\)精细处理的等比数列求求和复杂度。
(代码中的优化即使不加也是可以过的)
#define __AVX__ 1
#define __AVX2__ 1
#define __SSE__ 1
#define __SSE2__ 1
#define __SSE2_MATH__ 1
#define __SSE3__ 1
#define __SSE4_1__ 1
#define __SSE4_2__ 1
#define __SSE_MATH__ 1
#define __SSSE3__ 1
#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
#include <immintrin.h>
#include <emmintrin.h>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <bitset>
using namespace std;
const int MAXN=1.5e6+10;
int mod,T;
bitset<MAXN+1>vis;
int p[MAXN+1],cnt,mu[MAXN+1],N;
inline int Mod(long long a, int pp){
return a>=pp ? a%pp : a>=0 ? a : a+pp;
}
inline int add(int x,int y){return Mod( (1ll+x+y+mod-1ll),mod);}
inline int mul(int x,int y){return Mod(1ll*x*y,mod);}
void pretreatment(){
mu[1]=1;
for(int i=2;i<=MAXN;++i){
if(!vis[i])p[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&i*p[j]<=MAXN;++j){
vis[i*p[j]]=1;
if(Mod(i,p[j])==0)break;
mu[i*p[j]]=-mu[i];
}
}
}
inline int qpow(int a,int b){
if(!b)return 1;
if(a<=1||b==1)return a;
int res=1;
while(b){
if(b&1)res=mul(res,a);
a=mul(a,a);b>>=1;
}
return res;
}
inline int calc(int x,int y){
if(y==1)return x;
int res=calc(x,y>>1);
res=add(res,mul(res,qpow(x,y>>1)));
if(y&1)res=add(res,mul(x,qpow(x,y-1)));
return res;
}
inline int Calc(int x,int y){int ans=calc(x,y);return mul(ans,ans);}
int ssolve(int n,int d){
int res=0;
for(register int l=1;l<=n;++l){
if(!mu[l])continue;
res=add(res,mul(mu[l],Calc(qpow(d,l),n/l)));
}
return res;
}
int solve(int n){
int ans=0;
for(register int l=1;l<=n;l++){
ans=add(ans,ssolve(n/l,qpow(l,l)));
}
return ans;
}
signed main(){
scanf("%lld",&T);
pretreatment();
for(;T;T--){
scanf("%lld%lld",&N,&mod);
printf("%lld\n",solve(N));
}
return 0;
}
可以用整除分块减少循环中乘法的使用,对代码速度可能有一定的提升。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1.5e6+10;
int mod,T;
bitset<MAXN+1>vis;
int p[MAXN+1],cnt,mu[MAXN+1],N;
inline int Mod(long long a, int pp){return a>=pp ? a%pp : a>=0 ? a : a+pp;}
inline int add(int x,int y){return Mod( (1ll+x+y+mod-1ll),mod);}
inline int mul(int x,int y){return Mod(1ll*x*y,mod);}
inline int qpow(int a,int b){
if(!b)return 1;
if(a<=1||b==1)return a;
a=Mod(a,mod);
int res=1;
while(b){
if(b&1)res=mul(res,a);
a=mul(a,a);b>>=1;
}
return res;
}
inline int calc(int x,int y){
if(y==1)return x;
int res=calc(x,y>>1);
res=add(res,mul(res,qpow(x,y>>1)));
if(y&1)res=add(res,mul(x,qpow(x,y-1)));
return res;
}
inline int Calc(int x,int y){int ans=calc(x,y);return mul(ans,ans);}
int ssolve(int n,int d){
int res=0;
for(register int l=1,r;l<=n;l=r+1){
r=(n/(n/l));
int D=n/l;
for(int i=l;i<=r;++i){
if(!mu[i])continue;
res=add(res,mul(mu[i],Calc(qpow(d,i),D)));
}
}
return res;
}
int solve(int n){
int ans=0;
for(register int l=1,r;l<=n;l=r+1){
r=(n/(n/l));
int D=n/l;
for(int i=l;i<=r;++i)ans=add(ans,ssolve(D,qpow(i,i)));
}
return ans;
}
int main(){
scanf("%d",&T);
mu[1]=1;
for(register int i=2;i<=MAXN;++i){
if(!vis[i])p[++cnt]=i,mu[i]=-1;
for(register int j=1;j<=cnt&&i*p[j]<=MAXN;++j){
vis[i*p[j]]=1;
if(Mod(i,p[j])==0)break;
mu[i*p[j]]=-mu[i];
}
}
for(;T;T--){
scanf("%d%d",&N,&mod);
printf("%d\n",solve(N));
}
return 0;
}
经由大佬 @C3H5ClO 大佬证明,上面这份代码实际上是\(O(n\log n)\)的。
这里借用一下 @C3H5ClO 大佬的证明:
\]
\]
\]
\[\int\log\frac{n}{x}\mathrm{d}x=(\log n+1)x-x\log x+C
\]\[\int_0^n\log\frac{n}{x}\mathrm{d}x=n
\]
\]
(蒟蒻不会微积分惨被教育.jpg)
出这题的本意其实是想看看有没有吊打\(\text{std}\)的做法的,笔者推了很久并没有找到线性的做法。
【题解】 [EZEC-4]求和的更多相关文章
- 题解 P1630 【求和】
题目 发现题解都不够优雅,就自己来一篇 ( 以下除[代码]处代码,其余均为现场手打,如有误请与本蒟蒻联系 ) [分析] 首先,看清楚了,题目是 \(\sum_{i=1}^ai^b\) 的余数 ,而不是 ...
- [题解] [CQOI2007] 余数求和
题面 题解 考虑到这个等式\(a\bmod b = a - b * \lfloor\frac{a}{b}\rfloor\) 所以我们可以得到: \[ \begin{aligned} ans & ...
- BestCoder Round #86 部分题解
Price List 题意: 有n件商品,每天只能买一件,并且会记录账本,问有多少次一定记多了? 题解: 就是求和,最后如果大于和就输出1,否则0. 代码: #include <bits/std ...
- [CSP-S模拟测试96]题解
以后不能再借没改完题的理由不写题解了…… A.求和 求$\sum \sum i+j-1$ 柿子就不化了吧……这年头pj都不考这么弱智的公式化简了…… 坑点1:模数不定,可能没有2的逆元,那么只要先把乘 ...
- E题 - A+B for Input-Output Practice (IV)
Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Description You ...
- 【洛谷P1403】约数研究
题目大意:求\[\sum\limits_{i=1}^n\sum\limits_{d|i}1\] 题解:交换求和顺序即可. \[\sum\limits_{i=1}^n\sum\limits_{d|i}1 ...
- ACM-ICPC 2018 焦作赛区网络预赛 K题 Transport Ship
There are NN different kinds of transport ships on the port. The i^{th}ith kind of ship can carry th ...
- 【noip】noip201503求和(题解可能不完美,但绝对详细)
3. 求和 难度级别:B: 运行时间限制:1000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 题目描述 一条狭长的纸带被均匀划分出了n个格子,格子编号从1到n.每个格子 ...
- 【题解】P4091 [HEOI2016/TJOI2016]求和
[题解]P4091 [HEOI2016/TJOI2016]求和 [P4091 HEOI2016/TJOI2016]求和 可以知道\(i,j\)从\(0\)开始是可以的,因为这个时候等于\(0\).这种 ...
随机推荐
- Flutter —布局系统概述
老孟导读:此篇文章非常详细的讲解了 Flutter 布局系统的工作原理. 翻译自:https://itnext.io/flutter-layout-system-overview-c70bbe9ba9 ...
- LR监听Linux
1.准备 1)CentOS6.0 2)LR11.0 3)Linux安装包: xinetd-2.3.14-33.el6.i686.rpm (CentOS安装盘自带) rsh-server-0.17-60 ...
- 【转】Android 音量键+电源键 截屏代码小结
http://104zz.iteye.com/blog/1752961 原文地址:http://blog.csdn.net/hk_256/article/details/7306590 ,转载请注明出 ...
- 反射机制(Java)
反射机制 今天闲来无事,对反射机制http://www.cnblogs.com/jqyp/archive/2012/03/29/2423112.html阅读一番,整理了下这方面的知识以及自己的一些心得 ...
- 【IDEA】【SpringBoot】基于idea对springboot程序远程调试
一.开启远程调试前提:本地代码与服务器代码一致(实测:不关键的代码稍微有点不一样好像也不会有多大问题). 二.开启远程调试步骤 1.开发工具配置 idea端打开Edit configurations, ...
- centos开放指定端口
1.开启防火墙 systemctl start firewalld 2.开放指定端口 firewall-cmd --zone=public --add-port=1935/tcp ...
- hexo博客front-matter格式
通用格式 Front-matter 是文件最上方以 --- 分隔的区域,用于指定个别文件的变量,举例来说: --- title: Hello World date: 2013/7/13 20:46:2 ...
- 整理的网上的MySQL优化文章总结
MySQL优化 Linux优化 IO优化 调整Linux默认的IO调度算法. IO调度器的总体目标是希望让磁头能够总是往一个方向移动,移动到底了再往反方向走,这恰恰就是现实生活中的电梯模型,所以IO调 ...
- 学习go的一些笔记
反射 reflect type CfgSt struct{ Match *int64 `ini:"match"` Desc string `ini:"desc" ...
- Spring Boot 所有相关的配置信息
加载顺序 如上图所示,图片是从官网上截取的,这些配置信息都会加载,只不过顺序在前的会覆盖掉后面的 上图的所有配置信息都会以(key,value)的形式加载到Spring中的Environment中,也 ...