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;
    }
    
  • 相关阅读:
    【洛谷P3746】组合数问题
    jenkins部署docker
    ansible部署java及数据库环境
    UiPath从入门到精通视频教程
    jenkins安装配置及发布
    搭建uipath
    iostat、vmstat、iftop命令详解
    zabbix通过invoke调用监控服务可用性
    yearning_sql审核平台搭建
    vim操作
  • 原文地址:https://www.cnblogs.com/wzzyr24/p/11615386.html
Copyright © 2011-2022 走看看