先讲讲曲折的思路吧......
首先,应该是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....
慢着!你以为到这就结束了?
↘ ↓ ↙
→ 传送门 ←
↗ ↑ ↖
(完)