首页 > 其他分享 >P3312 [SDOI2014] 数表

P3312 [SDOI2014] 数表

时间:2024-09-12 09:14:31浏览次数:13  
标签:lfloor frac P3312 rfloor SDOI2014 sum 数表 sigma displaystyle

[SDOI2014] 数表

题目描述

有一张 \(n\times m\) 的数表,其第 \(i\) 行第 \(j\) 列(\(1\le i\le n\),\(1\le j\le m\))的数值为能同时整除 \(i\) 和 \(j\) 的所有自然数之和。给定 \(a\),计算数表中不大于 \(a\) 的数之和。

输入格式

输入包含多组数据。

输入的第一行一个整数 \(Q\) 表示测试点内的数据组数。

接下来 \(Q\) 行,每行三个整数 \(n\),\(m\),\(a\)(\(|a|\le 10^9\))描述一组数据。

输出格式

对每组数据,输出一行一个整数,表示答案模 \(2^{31}\) 的值。

样例 #1

样例输入 #1

2
4 4 3
10 10 5

样例输出 #1

20
148

提示

数据范围及约定

对于全部数据,\(1\le n,m\le 10^5\),\(1\le Q\le 2\times 10^4\)。

\(a_{i,j} = \displaystyle\sum\sigma(gcd(i,j))\)

\(ans=\displaystyle\sum_{i=1}^{n}\displaystyle\sum_{j=1}^{m}(a[i][j])*[a[i][j]\leq a]\)

先不考虑a的限制,把式子写出来。
对于单次询问,答案为\(\displaystyle\sum_{i=1}^n\displaystyle\sum_{j=1}^m\displaystyle\sum_{d|gcd(i,j)}d\)
把最后一重循环提前,直接枚举约数\(d\),可以得到\(\displaystyle\sum_{d=1}^{min(n,m)}\sigma(d)\displaystyle\sum_{i=1}^{\lfloor\frac n d\rfloor}\displaystyle\sum_{j=1}^{\lfloor\frac m d\rfloor}[gcd(i,j)==1]\)
后面的部分就是典型的反演。\(\displaystyle\sum_{d=1}^{min(n,m)}\sigma(d)\displaystyle\sum_{i=1}^{\lfloor\frac n d\rfloor}\displaystyle\sum_{j=1}^{\lfloor\frac m d\rfloor}\epsilon(gcd(i,j))\)然后\(\epsilon(n)=\displaystyle\sum_{d|n}\mu(d)\)带入得到\(\displaystyle\sum_{d=1}^{min(n,m)}\sigma(d)\displaystyle\sum_{i=1}^{\lfloor\frac n d\rfloor}\displaystyle\sum_{j=1}^{\lfloor\frac m d\rfloor}\displaystyle\sum_{d|i,d|j}\mu(d)\)
然后又是经典的枚举因子\(d\),\(\displaystyle\sum_{d=1}^{min(n,m)}\sigma(d)\displaystyle\sum_{e=1}^{min(\lfloor\frac n d\rfloor,\lfloor\frac n d\rfloor)}\mu(e)\lfloor\frac n {de}\rfloor\lfloor\frac m {de}\rfloor\)
根据之前的题目,整除分块的部分最好放到前面。枚举\(f=d\times e\)

\(\displaystyle\sum_{f=1}^{min(n,m)}\lfloor\frac n {f}\rfloor\lfloor\frac m {f}\rfloor \displaystyle\sum_{d|f}\sigma(d)\mu(\frac f d)\)后面这部分能够预处理。如果没有\(a\)的限制的话,这题就到此为止了。(我咋感觉到此为止也算是有点难度)

考虑一下带上\(a\)的限制会产生什么变化。可以注意到其实\(a\)限制的是函数\(\sigma\),对于后面部分的循环,我们只计算\(\sigma(d)\geq a\)的贡献。
于是考虑函数\(g(f)=\displaystyle\sum_{d|f}\sigma(d)\mu(\frac f d)\),只需要对每个\(a\)得到相应的函数\(g_a(f)\)就可以计算答案。于是考虑维护\(g(f)\),将询问按照\(a\)排序,\(n,m\)的大小对于答案无关紧要,重要的是当\(a\)变化时如何快速维护好对应的\(g_a(f)\)。考虑当\(a\)变大时,\(g_a(f)\)的变化,可以发现有一些\(\sigma(d)\)原本不对\(g\)产生贡献,经过变化后会产生贡献。
先预处理\(\sigma\)函数,然后顺着对每个询问,\(a_i\)到\(a_{i+1}\),需要枚举出所有的\(a_i<\sigma(j)\leq{a_{i+1}}\),于是很明显需要把\(\sigma(j)\)按照大小排序。每个原本不计算的\(\sigma(j)\)在需要计算后会对\(g(1*j),g(2*j)\)这些倍数位置的\(g_a(f)\)都产生影响,每一个\(1\leq j\leq n\)都需要枚举倍数,总体复杂度\(n\ ln\ n\),然后看看维护的\(g_a(f)\)的修改查询需求,单点修改和区间查询,所以直接树状数组就可以。

数论分块复杂度\(O(\sqrt n)\),共计\(q\)次,每次需要查询的\(O(log\ n)\)所以总体是\(O(q\sqrt n\ log\ n+n\ln n\ log\ n)\)
其实思路不好出,因为不知道如果不先处理\(a\)的限制的话后面是否能够补救。如果这个思路能够具有一定的普适性的话两种可能,一种是这里\(a\)的限制具有一定的特殊性还有一种就是\(a\)的限制先考虑就是没法做。其实应该是两种都有。\(a\)的限制其实说到底就是对于\(\sigma\)函数的大小的限制,而事实上从开始就能够非常明显的发现函数\(\sigma\)会出现在答案中。而莫反的答案形式基本上都是整除分块在前,数论函数在后。既然\(a\)的变化只影响了\(\sigma\)的贡献,那么只需要和这题的做法一样修改后面的部分即可,毕竟本来莫反的数论函数的部分就是预处理得到的。

那其实能够总结得到一个结论,对于与统计的结果相关的数论函数的部分的限制可以考虑对于限制进行动态维护。这个是一个思路。主要是第一眼看到这个\(a\)的限制这题我是完全没有思路,一个很重要的原因就是想着先处理\(a\)的限制而不是先不考虑这个限制。毕竟如果不考虑\(a\)的限制的话我其实做的就不是这题了。。但是现在根据上面的分析其实能够得到对于数论函数的限制大概率可以通过反演将其移动至整除分块后面的部分,这样就能够预处理和动态维护了。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read()
{
    char c=getchar();ll a=0,b=1;
    for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
    for(;c>='0'&&c<='9';c=getchar())a=a*10+c-'0';
    return a*b;
}
const ll N=1e5;
ll tr[N+1],prime[N+1],cnt,mu[N+1],g[N+1],vis[N+1],Q,ans[20001];
struct D
{
    ll id,v;
}sigma[N+1];
struct query
{
    ll n,m,a,id;
}q[20001];
inline ll lowbit(ll x)
{
    return x&(-x);
}
inline void add(ll i,ll x)
{
    while(i<=N)
    {
        tr[i]+=x;
        i+=lowbit(i);
    }
}
inline ll ask(ll i)
{
    ll ans=0;
    while(i>0)
    {
        ans+=tr[i];
        i-=lowbit(i);
    }
    return ans;
}
bool mycmp1(D a,D b)
{
    return a.v<b.v;
}
void init()
{
    mu[1]=1;
    for(ll i=2;i<=N;i++)
    {
        if(!vis[i])prime[++cnt]=i,mu[i]=-1;
        for(ll j=1;j<=cnt&&i*prime[j]<=N;j++)
        {
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)
            {
                mu[i*prime[j]]=0;
                break;
            }
            mu[i*prime[j]]=mu[i]*mu[prime[j]];
        }
    }
    for(ll i=1;i<=N;i++)
    {
        sigma[i].id=i;
        for(ll j=i;j<=N;j+=i)
        {
            sigma[j].v+=i;
        }
    }
    sort(sigma+1,sigma+1+N,mycmp1);
}
bool mycmp2(query a,query b)
{
    return a.a<b.a;
}
bool mycmp3(query a,query b)
{
    return a.id<b.id;
}
void solve(ll x)
{
    ll n=q[x].n,m=q[x].m;
    for(ll l=1,r=0;l<=min(n,m);l=r+1)
    {
        r=min(n/(n/l),m/(m/l));
        ans[q[x].id]+=(n/l)*(m/l)*(ask(r)-ask(l-1));
    }
}
int main()
{
    init();
    Q=read();
    for(ll i=1;i<=Q;i++)
    {
        q[i].n=read(),q[i].m=read(),q[i].a=read();
        q[i].id=i;
    }
    sort(q+1,q+1+Q,mycmp2);
    ll now=1;
    for(ll i=1;i<=Q;i++)
    {
        while(now<=N&&sigma[now].v<=q[i].a)
        {
            for(ll j=1;j*sigma[now].id<=N;j++)
            {
                add(j*sigma[now].id,mu[j]*sigma[now].v);
        // cout<<"?"<<endl;
            }
            now++;
        }
        ans[q[i].id]=0;
        solve(i);
    }
    sort(q+1,q+1+Q,mycmp3);
    for(ll i=1;i<=Q;i++)
    {
        cout<<ans[i]%(1LL<<31)<<endl;
    }
    return 0;
}

最近写代码运气这么好的,都一遍过的。
应该是这些莫反的题目没有什么代码细节导致的吧

标签:lfloor,frac,P3312,rfloor,SDOI2014,sum,数表,sigma,displaystyle
From: https://www.cnblogs.com/HLZZPawa/p/18409512

相关文章

  • 从计组中从重温C中浮点数表示及C程序翻译过程
    目录移码​编辑 传统浮点表示格式浮点数的存储(ieee754)->修炼内功例子:  ​编辑浮点数取的过程  C程序翻译过程移码 传统浮点表示格式浮点数的存储(ieee754)->修炼内功根据国际标准IEEE(电⽓和电⼦⼯程协会) 32位例子:  64位  IEEE754对有效......
  • [题解]P3311 [SDOI2014] 数数
    P3311[SDOI2014]数数看到多模式匹配,我们考虑先对所有模式串建立AC自动机。然后发现这道题和P4052文本生成器(题解)挺像的,后者让求包含至少一个模式串的个数,这道题让求一个也不包含的个数,这个就是一个用不用\(26^m\)去减的问题,很好处理。但这道题还多了一个条件,“幸运数”必须\(......
  • C++虚函数表、地址详解(x86/x64)
    参考博文:c++虚函数表、地址详解-CSDN博客本文在上述博文的基础上,补充了x64下的验证代码。一.什么是虚函数表,它有什么特点?        虚函数大家都知道是基本用于实现多态的,当父类指针指向子类对象的时候,如何确定调用的函数是父类里的还是子类里面的,这就要用到虚函数表......
  • 设计一位字段结构存储下面信息。 字体ID:0~255之间的一个数 字体大小:0~127之间的一个数
    /设计一位字段结构存储下面信息。字体ID:0~255之间的一个数字体大小:0~127之间的一个数对齐:0~2之间的一个数表示左对齐,居中,右对齐加粗:开(1)或闭(0)斜体:开(1)或闭(0)在程序中使用该结构来打印字体参数,并使用循环菜单来让用户改变参数。例如,该程序的一个运行示例如下:IDSIZEALIGNMEN......
  • 计算机组成与体系结构-浮点数表示
    定点数:是一种在计算机中表示和处理实数的方法,其中,小数点的位置是固定的,不会随着数值的大小而变化。浮点数:是计算机中用于表示实数的一种数据类型。小数点位置浮动浮点数表示阶码(指数部分):决定了浮点数可以表示的范围。阶码越长,可以表示的指数范围就越大尾数(有效数......
  • 虚函数表 和 虚函数指针
     虚函数指针vptr大小x86平台下为4个字节,x64平台下为8个字节例题:涉及内存对齐 答案:32位miaoage=264位miaoage=1核心在这句话上p【1】=q【1】;由于两个子类都继承自有虚函数的基类因此都带有虚表指针首先基类中只有一个int432位下,虚表指针也是4......
  • Linux C进阶 —— 浮点数表示(IEEE标准754)
    1.IEEE标准754  IEEE标准754制订了表示浮点数的标准,解决了浮点数在不同机器上的可移植性。该标准使用      F=(-1)s *M*2E  形式来表示一个实数。  s:表示符号,1为负实数,0为正实数;  M:表示尾数,是一个二进制小数;  E:表示阶码,对......
  • 第五章:多态、抽象类、虚函数、虚函数表
    一、虚函数:1.1虚函数的概念:被virtual修饰的类成员函数称为虚函数。通过重写虚函数,可以实现多态。        1.2如何重写虚函数:派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表类型完全相同),称子类的虚函数重......
  • IEEE754浮点数表示形式
    IEEE754浮点数表示形式IEEE754浮点数官方文档:https://ieeexplore.ieee.org/document/8766229浮点数的上述表示形式,既没有规定阶码和尾数的位数,也没有规定阶码和尾数采用的机器码形式(原码、反码、补码和移码)。实际上,直到20世纪80年代初,浮点数表示形式还没有统一标准,不同厂商计......
  • 利用 word VBA 将投标文件偏离参数表列数据拷贝至技术偏差表中
    使用vba将正偏离参数表的第一列信息复制粘贴至对应的技术偏离表的第4列中。需要同时打开两个word文件,在技术偏差表中打开VBE(可以用ctrl+f11快捷键),插入模块。忽略格式的方式,SubCopyDataToTable()Windows("正偏离参数表.docx").ActivateFori=1ToActiveDoc......