飛行大白鯊—現代版灰燼戰線原創角色:殲-15戰鬥機
阿新 • • 發佈:2022-03-31
同餘最短路
什麼神仙演算法
這類問題的關鍵在於建模,頭大了,主要總結幾個題的思路技巧吧。
(標題有 Link)
P3403 跳樓機
明顯的樓層數只能在 \([1,k]\) 之間,因為可以回到第一層,那麼問題轉化一下:
滿足 \(ax+by+cz \equiv i \pmod k\) 的 \(i\) 有幾個。
對於每一個 \(i\) ,我們可以把它表示為 \(i=xn+m\),\(m\) 為餘數,可以用 \(y,z\) 去湊出 \(m\) 這個餘數,任何可以在 \([0,x-1]\) 範圍內統計餘數就可以得出樓層數,設 \(dis[i]\) 表示用若干個 \(y,z\) 能組成的餘數(或者說能到達的位置,餘數 \(\in (0,x-1]\)
所以這樣連邊:
\[Add\_edge(m,(m+y)\bmod a,y) \] \[Add\_edge(m,(m+z)\bmod a,z) \]跑一遍最短路,得到了 \([0,x)\) 範圍內的能組成的餘數 \(dis[i],i\in[0,x)\)
因為 \(x\) 可能比 \(k\) 大,所以只選取餘數比 \(k\) 小的部分,看 \(k-dis[i]\) 中有幾個 \(a\)
/* Knowledge : Rubbish Algorithm Work by :Gym_nastics Time : O(AC) */ #include<bits/stdc++.h> #define int long long using namespace std; const int INF=0x3f3f3f3f; const int N=1e6+6; int read() { int x=0,f=0;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) f|=(ch=='-'); for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch&15); return f?-x:x; } void print(int x) { if(x<0) putchar('-'),x=-x; if(x>9) print(x/10); putchar(x%10+48); } bool vis[N]; int dis[N],head[N],cnt;struct node{int v,w,nxt;}e[N]; void Add_edge(int u,int v,int w){e[++cnt]=(node){v,w,head[u]};head[u]=cnt;} int h,a,b,c; void SPFA(){ queue<int>q;memset(dis,INF,sizeof dis); dis[1]=1;q.push(1);vis[1]=true; while(!q.empty()){ int u=q.front();q.pop();vis[u]=false; for(int i=head[u];i;i=e[i].nxt){int v=e[i].v; if(dis[v]>dis[u]+e[i].w){ dis[v]=dis[u]+e[i].w; if(!vis[v]) q.push(v),vis[v]=true; } } } } signed main() { h=read();a=read();b=read();c=read();if(a==1 or b==1 or c==1) return print(h),0; for(int i=0;i<a;i++)Add_edge(i,(i+b)%a,b),Add_edge(i,(i+c)%a,c); SPFA();int Ans=0; for(int i=0;i<a;i++)if(h>=dis[i]) Ans+=(((h-dis[i])/a)+1); return print(Ans),0; }
AT3621 [ARC084B] Small Multiple
是個人都知道暴力不可過……
需要知道一點的是:一個數通過不停 \(+1\) 和 \(\times 10\) 能構成任何數,並且 \(+1\) 對和的影響是 \(0\),\(\times 10\) 的影響是 \(0\),設 \(dis[i]\) 表示 \(x \bmod k=i\) 的最小數字和,則 \(dis[0]\) 表示整除的情況。那就比較簡單了。
\[Add\_edge(i,i+1,1) \] \[Add\_edge(i,(i\times 10)\bmod k,0) \]注意 \(1\) 的數字和是 \(1\),跑一遍最短路。
/*
Knowledge : Rubbish Algorithm
Work by :Gym_nastics
Time : O(AC)
*/
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=1e6+6;
int read() {
int x=0,f=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) f|=(ch=='-');
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch&15);
return f?-x:x;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+48);
}
int head[N],cnt;struct node{int v,w,nxt;}e[N];
void Add_edge(int u,int v,int w){e[++cnt]=(node){v,w,head[u]};head[u]=cnt;}
int k;int dis[N];bool vis[N];
void SPFA(){
memset(dis,INF,sizeof dis);
dis[1]=1;vis[1]=1;queue<int>q;q.push(1);
while(!q.empty()){
int u=q.front();q.pop();vis[u]=0;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
if(dis[v]>dis[u]+e[i].w){
dis[v]=dis[u]+e[i].w;
if(!vis[v]) vis[v]=1,q.push(v);
}
}
}
}
signed main() {
k=read();
for(int i=1;i<k;i++)
Add_edge(i,(i+1)%k,1),Add_edge(i,(i*10)%k,0);
SPFA();print(dis[0]);
return 0;
}