【洛谷】P2371 [国家集训队]墨墨的等式(屠版题)

先讲讲曲折的思路吧......

首先,应该是CRT之类的东西,乱搞

不行......打了打草稿,发现有解的情况是gcd(a1,a2.....an)|B,于是可以求gcd然后O(n)查询?但是B的范围直接劝退...

(这是cyr大佬讲的“烂大街”的套路(来自ctsc哦))于是我翻开了课件和百度

课件:最短路乱搞 百度:同余最短路???

说白了还是数论喽???

题解:

 在课件和草稿纸的帮助下,我成功地推出了一个结论:

对于最后一个a,也就是an,设anxn=q,假设前面的数加起来是S,则S+q=B;

对于不同的Xn,q总是一个等差数列。

所以:(B-S)%an=0;

∴B %an=S%an.(B>S)

所以发现:如果式子可以达到S+q,那么式子一定也能达到S+(i*q)。

所以我们的任务就是求对于每一个a的最小S。

怎么求呢?

首先找出ai中的最小值mn

因为要求剩余系嘛,剩余我们可以连边,在i与(a[i]+i)%mn之间连一条权值为a[i]的单向边,表示在mod mn=i到mod mn=(i+ai)%mn之间,可以通过a[j]的代价达到,于是这样就处理出了每一个点的最小剩余系

于是,对于每一个S,答案就是Σ(dis[i]<a[j])(a[i]-dis[i])/mn+1

于是,这题的代码就没有什么含金量了(虽然我调了整整一上午+1h,而且整整屠了一个半的版)

坑点1:会爆long long

坑点2:无脑开long long会MLE

坑点3、long long 和int不能乱用

代码1(跑得快的)(因为没有邻接表,直接从前面一个点得出下面一个点)

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5e6+6;
const int maxm=5e5+5;
const ll inf=1e12+1;
int n;
int tot;
ll l,r;
int mn=maxm+5;
int a[maxm];
int cnt,head[maxm];
inline  ll read()
{
    ll f=1,x=0;char s=getchar();
    while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
    while(s<='9'&&s>='0'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
struct edge
{
    int to,next,dis;
}e[maxn];
inline void addedge(int from,int to,int dis)
{
    e[++cnt].next=head[from];
    e[cnt].to=to;
    e[cnt].dis=dis;
    head[from]=cnt;
}
ll dis[maxn];
int vis[maxn];
struct cmp
{
    bool operator() (int a,int b)
    {
        return dis[a]>dis[b];
    }
};
//queue < int > q;
priority_queue < int , vector < int > , cmp > q;
void spfa(int s)
{
    memset(dis,0x3f,sizeof(dis));
    q.push(s);
    dis[s]=0;
    vis[s]=1;
    while(!q.empty())
    {
        int u=q.top();
        q.pop();
        vis[u]=0;
        for(int i=1;i<=n;i++)
        {
            int v=(u+a[i])%mn;
            if(dis[v]>dis[u]+a[i])
            {
                dis[v]=dis[u]+a[i];
                if(vis[v]==0)
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
}
ll query(ll x)
{
    ll res=0;
    for(int i=0;i<=mn;i++)
    {
        if(dis[i]<=x)
        {
            res+=(x-dis[i])/mn+1;
        }
    }
    return res;
}
int main()
{
    n=read();l=read();r=read();//
    for(int i=1;i<=n;i++)
    {
        ll x;
        x=read();
        if(x==0)
        continue;
        a[++tot]=x;
        mn=min(mn,a[i]);

    }
    /*for(ll i=0;i<=mn;i++)
    {
        for(int j=1;j<=tot;j++)
        {
            if(a[j]!=mn)
            addedge(i,(i+a[j])%mn,a[j]);
        }
    }*/
    spfa(0);
    printf("%lld",query(r)-query(l-1));
    return 0;
}

代码2:(跑得慢的)

把上面魔改一下就行了233....

慢着!你以为到这就结束了?

↘         ↓          ↙

→    传送门     ←

↗         ↑          ↖

(完)

原文地址:https://www.cnblogs.com/ajmddzp/p/11341198.html