zoukankan      html  css  js  c++  java
  • 两道趣题

    好吧,估计以后会觉得这两道题很沙雕,但我现在实在太菜了……
    注:([n])(n)的下取整。

    UPD on 2020/4/23:sb题!sb题!sb题!


    pro.1 给定(n),求$ sumlimits_{i = 1}^n {left[ {dfrac{n}{i}} ight]} $,数据范围(1 leqslant n leqslant 10^{12})

    首先(O(n))肯定凉了,所以得找规律(又是找规律,害怕.jpg
    根据 看std 手模与观察,可以发现在一段区间内的值是相等的,最明显的就是在(left[ {frac{n}{{n/2}}} ight])之后所有的值都是(1),从这里入手,进一步发现连续为同一个值的个数为(left[ {frac{n}{{[n/i]}}} ight]-i+1)个,于是我们便可以在接近(O(sqrt{n}))的复杂度内求解之。
    在考场上,想到这里一般这道题就结束了,但我不禁还是要刨根问底一下,为什么是这样呢?毕竟如果每次总凭借直觉来做题是没有多大提高的。
    其实(这词用得好像我一下就想出来了一样2333),在每次求得一个商([n/i]=d)时,我们实际上是在回答这样一个问题:“在(n)中有多少个(i)?(当然答案是(d))”,但是我们要求的东西却是:“有多少个连续的(d)?”首先我们知道,在(n)中最多有([n/d])(d),但是在(i)之前有比(d)更大的商,所以要把比它大的个数减去,而这个个数刚好是(i-1)个,于是答案便呼之欲出。

    update:最近在学莫比乌斯反演的时候惊喜地发现,这个东西叫 整除分块 ,这样的话这两道题的来源就都清楚了。(果然很沙雕
    UPD on 2020/4/23:本来是不想更新的……但本着看看自己有多菜的想法还是来更新了,上面的证明(好吧,就不算证明)太弱智了,OI-wiki 上面的证明很漂亮,可以去看看,我这沙雕东西就当公开处刑吧。


    pro.2 给定(n),数列({ {a_n}})与区间([minb,maxb]),求有多少个(b)使得方程(sumlimits_{i = 1}^n {{a_i}{x_i}} = b)有非负整数解,其中(b in [minb,maxb])。数据范围:(n leqslant 12,a_i leqslant 5 imes 10^5,1 leqslant minb < maxb leqslant 10^{12})

    这题是真的毒瘤,一开始还在想什么高斯消元和线性筛之类的东西……结果考完才知道是个图论?(?????)然后问了下学长才知道是国家集训队的题?(?????)然后sys说他当初考的时候30min就A了?(?????)
    好吧,我死得明明白白。
    经过一下午的 看题解 冷静思考,终于知道怎么做了(嘤嘤嘤
    这类题还有个专门的名字叫 同余最短路同余类BFS ,虽然此类题可以用完全背包来做,但遇到这种感人的数据范围((10^{12})啊!(破音))便马上歇菜。所以我们得想出一种复杂度不依赖于(b)的做法。
    首先,我们要考虑同余类这个东西。比方说3吧,我们可以把所有的自然数都用(3k,3k+1,3k+2)表示出来,所以在这道题中,我们如果想要把区间里的所有数都要表示出来,就要用到剩余类的思想。首先我们得在数列({ {a_n}})找个最小的,就设成(p)吧,然后考虑它的剩余类(kp,kp+1,...,kp+q),如果我们找到对于剩余类中的每一个数,上面的那个方程可以凑出来的最小值,那么最小值之后的数便都可以凑出来了,这样的话就可以进行统计个数了。至于为什么要选数列里最小的,是因为这样可以使后面建图的时候点数更少。
    这样可能还是有些抽象,再具体点,比方说(3x+5y=b)这个方程(为方便把(x_2)写成(y)),那么我们如果分别求出了当(x,y)为自然数时左边那一坨可以表示出的形如(3k,3k+1,3k+2)的最小值(在这里是(0,10,5)),那么之后的什么(3,6,9)啊,(13,16,19)啊,(8,11,14)啊都能表示出来。所以如果你要问([0,n])里有多少个数能被表示出来时,答案就是([(n-0)/3]+1+[(n-10)/3]+1+[(n-5)/3]+1)了(因为同余类互异,所以不会有交叉的情况)。
    那剩下的问题就是如何求这些最小值了。这里要想到最短路(别问我怎么想到的),考虑对于(mod p)的每一个余数(i)作为一个点,(dis[i])就是到这个点的最短距离,实际上就表示能凑出的最小的数。那么如何建边呢?我们可以对于每个点(i)((i+a[j])mod p)连一条权值为(a[j])的有向边,代表从剩余类中的一种情况到另一种情况要加上的值。最后跑一遍dij或者SPFA就行了。注意,dij要加heap优化才能过,但由于此题特殊的建边方式,SPFA并不会被卡(相反它跑得很好),所以就用了SPFA。还有,由于之前的求答案只能求从0开始的区间的答案,所以我们还得减去([0,minb-1])的答案。并且这里还有个恶心的地方,输入的方程系数有可能为0,得特判。
    经过不懈的努力,这道国家集训队的图论+数论毒瘤题就被解决了。(orz国集神仙们
    PS.这里还有个骚操作,可以不用显式建边,在跑最短路的时候现算就行(但我并不会写)。
    代码:(调得我心力交瘁)

    #include <cstdio>
    #include <queue>
    #include <cstring>
    #define MIN(x,y) x<y?x:y
    using namespace std;
    typedef long long ll;
    struct Edge
    {
        int to,nxt;
        ll w;
    }e[15*500005];
    ll minb,maxb,ans,a[15],minn=5000005,dis[500005];
    int n,head[500005],cnt,tot,temp;
    bool vis[500005];
    inline void add(int u,int v,ll w) {e[++cnt]=(Edge){v,head[u],w}, head[u]=cnt;}
    void SPFA()
    {
        memset(dis,0x3f,sizeof(dis));
        dis[0]=0;
        queue<int> q;
        q.push(0);
        while(!q.empty())
        {
            int u=q.front(); vis[u]=0; q.pop();
            for(int i=head[u];i;i=e[i].nxt)
            {
                int v=e[i].to;
                if(dis[v]>dis[u]+e[i].w)
                {
                    dis[v]=dis[u]+e[i].w;
                    if(!vis[v]) {q.push(v); vis[v]=1;}
                }
            }
        }
    }
    int main()
    {
        scanf("%d%lld%lld",&n,&minb,&maxb);
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&temp);
            if(!temp) continue;
            a[++tot]=temp;
            minn=MIN(minn,a[tot]);
        }
        for(int i=0;i<minn;++i)
            for(int j=1;j<=tot;++j)
                if(a[j]!=minn) add(i,(i+a[j])%minn,a[j]);
        SPFA();
        for(int i=0;i<minn;++i)
            if(dis[i]<=maxb)
            {
                ans+=(maxb-dis[i])/minn+1;
                if(dis[i]<minb) ans-=(minb-1-dis[i])/minn+1;
            }
        printf("%lld",ans);
        return 0;
    }
    
  • 相关阅读:
    python--模块与包
    内置函数 的总结
    迭代器 生成器 列表推导式 生成器表达式的一些总结
    函数的有用信息 带参数的装饰器 多个装饰器装饰一个函数
    函数名的应用(第一对象) 闭包 装饰器
    动态参数 名称空间 作用域 作用域链 加载顺序 函数的嵌套 global nonlocal 等的用法总结
    函数的初识 函数的返回值 参数
    文件操作 常用操作方法 文件的修改
    遍历字典的集中方法 集合的作用 以及增删查的方法
    计算机硬件的小知识
  • 原文地址:https://www.cnblogs.com/wzzyr24/p/11615386.html
Copyright © 2011-2022 走看看