1. Elevator (Gym-241680 E)
题意:
一个高度为
h
h
h 的电梯,初始位置在第一层。电梯有四个按钮。
- 向上移动 a a a 格
- 向上移动 b b b 格
- 向上移动 c c c 格
- 返回第一层
问 1 ~ h 1~h 1~h 层中有多少层是可达的。 ( 1 ≤ h ≤ 1 0 18 , 1 ≤ a , b , c ≤ 100000 ) (1\leq h\leq 10^{18},1\leq a,b,c\leq 100000) (1≤h≤1018,1≤a,b,c≤100000)
思路:
观察数据范围可以发现,
a
、
b
、
c
a、b、c
a、b、c 数据范围比较小,可以从此处着手。
不难发现,如果 2 2 2 可以达到,则 2 + a 2+a 2+a、 2 + 2 ∗ a 2+2*a 2+2∗a、 2 + 3 ∗ a 2+3*a 2+3∗a… 均可到达。即我们以 a a a 作为基底,如果 x ( x < a ) x\ (x<a) x (x<a) 可以到达,则 y ( y % a = x ) y\ (y\%a=x) y (y%a=x) 也可到达。
因此问题转化为对于 x ( x < a ) x(x<a) x(x<a) 来说,最小的可达的 y y y 是多少? ( y % a = x ) (y\%a=x) (y%a=x) 既然问题变成了求最小,那么不难想到使用最短路来解决这个问题。
我们枚举 x ( 0 ≤ x < a ) x\ (0\leq x<a) x (0≤x<a),令 x x x 与 ( x + b ) % a (x+b)\%a (x+b)%a 和 ( x + c ) % a (x+c)\%a (x+c)%a 连边,边权分别为 b b b、 c c c。然后起点为 1 1 1,即可求出到达所有 x x x 时的最小值。
求取答案时就可以枚举 x x x,然后求 d i s [ x ] ~ h dis[x]~h dis[x]~h 中有多少个 m o d a = x mod\ a=x mod a=x 的值即可。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#define rep(i,a,b) for(int i = a; i <= b; i++)
typedef long long ll;
typedef double db;
const int N = 1e5+100;
const int M = 1e6+100;
const ll inf = 1e15;
const db EPS = 1e-9;
using namespace std;
ll h,a,b,c,dis[N];
int head[N],tot,vis[N];
struct Node{
int to,next;
ll w;
}e[M];
void init() {tot = 1;}
void add(int x, int y, ll w){
e[++tot].to = y, e[tot].next = head[x], head[x] = tot, e[tot].w = w;
}
void dijkstra(int s){
priority_queue<pair<ll,int> > q;
while(q.size()) q.pop();
rep(i,0,a-1) dis[i] = inf, vis[i] = 0;
dis[s%a] = 1;
q.push(make_pair(-dis[s%a],s%a));
while(q.size()){
int x = q.top().second; q.pop();
if(vis[x]) continue;
vis[x] = 1;
for(int i = head[x]; i; i = e[i].next){
int y = e[i].to;
if(dis[y] > dis[x] + e[i].w){
dis[y] = dis[x] + e[i].w;
q.push(make_pair(-dis[y],y));
}
}
}
}
int main()
{
freopen("elevator.in","r",stdin);
freopen("elevator.out","w",stdout);
scanf("%lld%lld%lld%lld",&h,&a,&b,&c);
init();
rep(i,0,a-1){
add(i,(i+b)%a,b);
add(i,(i+c)%a,c);
}
dijkstra(1);
ll ans = 0;
rep(i,0,a-1){
if(dis[i] == inf || dis[i] > h) continue;
ans += 1ll + (h-dis[i])/a;
}
printf("%lld\n",ans);
return 0;
}
2. Sums (Gym-100753 M)
题意:
n
n
n 个数,
k
k
k 组询问。每组询问给出一个
b
b
b,问能否用这
n
n
n 个数组成
b
b
b。
(
1
≤
a
i
≤
5000
,
1
≤
b
i
≤
1
0
9
)
(1\leq a_i\leq 5000,1\leq b_i\leq 10^9)
(1≤ai≤5000,1≤bi≤109)
思路:
n
2
n^2
n2 建图,直接跑最短路得到答案即可。
代码:
//以a为循环,求出 0~a-1 中每一个数字第一次被到达的数字
#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#define rep(i,a,b) for(int i = a; i <= b; i++)
typedef long long ll;
typedef double db;
const int N = 1e5+100;
const ll inf = 1e15;
const db EPS = 1e-9;
using namespace std;
ll dis[N];
int n,a[N],vis[N],k;
void dijkstra(int s){
priority_queue<pair<ll,int> > q;
while(q.size()) q.pop();
rep(i,0,a[1]-1) dis[i] = inf, vis[i] = 0;
dis[0] = 0;
q.push(make_pair(0,0));
while(q.size()){
int x = q.top().second; q.pop();
if(vis[x]) continue;
vis[x] = 1;
rep(i,2,n){
int y = (x+a[i])%a[1];
if(dis[y] > dis[x] + a[i]){
dis[y] = dis[x] + a[i];
q.push(make_pair(-dis[y],y));
}
}
}
}
int main()
{
scanf("%d",&n);
rep(i,1,n) scanf("%d",&a[i]);
dijkstra(0);
scanf("%d",&k);
rep(i,1,k){
int tp,hp; scanf("%d",&tp);
hp = tp%a[1];
if(tp >= dis[hp]) printf("TAK\n");
else printf("NIE\n");
}
return 0;
}
3. 墨墨的等式 (Bzoj-2118)
题意:
对于
a
1
∗
x
1
+
a
2
∗
x
2
+
.
.
.
+
a
n
∗
x
n
=
B
a_1*x_1+a_2*x_2+...+a_n*x_n=B
a1∗x1+a2∗x2+...+an∗xn=B 是否存在非负整数解。给出
n
、
a
i
n、a_i
n、ai 以及
B
B
B 的范围,求出有多少个
B
B
B 使等式存在非负整数解。
(
1
≤
N
≤
12
,
0
≤
a
i
≤
5
∗
1
0
5
,
1
≤
B
M
i
n
≤
B
M
a
x
≤
1
0
12
)
(1\leq N\leq 12,0\leq a_i\leq 5*10^5,1\leq BMin\leq BMax\leq 10^{12})
(1≤N≤12,0≤ai≤5∗105,1≤BMin≤BMax≤1012)
思路:
与上面题目做法一致,选取最小的
a
a
a 作为基底,然后
n
2
n^2
n2 建图,求出第一次到达
0
~
a
−
1
0~a-1
0~a−1 中各数字的最小值,然后统计答案即可。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const int N = 20+100;
const int M = 12*5*1e5+100;
const db EPS = 1e-9;
using namespace std;
int n,head[M],tot,vis[M];
ll l,r,a[N],ans,dis[M];
struct Edge{
int to,next,w;
}e[M];
void add(int x,int y,int z){
e[++tot].to = y, e[tot].next = head[x], head[x] = tot, e[tot].w = z;
}
priority_queue< pair<ll,int> > q;
void dijkstra(int s){
while(q.size()) q.pop();
memset(dis,0x3f,sizeof dis);
dis[s] = 0; //第一次到达%a[1] == 0的点为0
q.push(make_pair(0,s));
while(q.size())
{
int x = q.top().second;
q.pop();
if(vis[x]) continue;
vis[x] = 1;
for(int i = head[x]; i; i = e[i].next){
int y = e[i].to, z = e[i].w;
if(dis[y] > dis[x]+z){
dis[y] = dis[x]+z;
q.push(make_pair(-dis[y],y));
}
}
}
}
int main()
{
scanf("%d%lld%lld",&n,&l,&r);
int ctt = 0;
rep(i,1,n){
ll tpp; scanf("%lld",&tpp);
if(tpp != 0) a[++ctt] = tpp;
}
n = ctt;
if(n == 0) printf("0\n");
else{
rep(i,1,n)
if(a[1] > a[i]) swap(a[1],a[i]); //找到最小的,降低复杂度
rep(i,0,a[1]-1){
rep(j,2,n){
add(i,(i+a[j])%a[1],a[j]);
}
}
dijkstra(0);
rep(i,0,a[1]-1){
ll tp1 = 0, tp2 = 0;
if(l-1-dis[i] >= 0)
tp1 = (l-1-dis[i])/a[1]+1;
if(r-dis[i] >= 0)
tp2 = (r-dis[i])/a[1]+1;
ans += tp2-tp1;
}
printf("%lld\n",ans);
}
return 0;
}