问题 A: 借书
题目描述
Dilhao一共有n本教科书,每本教科书都有一个难度值,他每次出题的时候都会从其中挑两本教科书作为借鉴,如果这两本书的难度相差越大,Dilhao出的题就会越复杂,也就是说,一道题的复杂程度等于两本书难度差的绝对值。
这次轮到ldxxx出题啦,他想要管Dilhao借m本书作为参考去出题,Dilhao想知道,如果ldxxx在Dilhao给出的m本书里挑选难度相差最小的两本书出题,那么ldxxx出的题复杂程度最大是多少?
输入
第一行是n和m。
接下来的n行,每行一个整数ai表示第i本书的难度。
输出
一个整数为ldxxx出的题复杂程度的最大值。
样例输入
6 3
5
7
1
17
13
10
样例输出
7
提示
【样例解释】
Dilhao给了ldxxx难度为1,10,17的三本书,ldxxx挑选难度为10和17的两本书,出题复杂度为7;
如果Dilhao给出其他任何三本书,其中的两本书难度差的最小值都小于7,所以ldxxx出题的最大复杂程度为7。
【数据说明】
对于 30%的数据: 2<=n<=20;
对于 60%的数据: 2<=n<=1000;
对于 100%的数据: 2<=n<=100000, 2<=m<=n, 0<=ai<=1000000000。
如果Dilhao给出其他任何三本书,其中的两本书难度差的最小值都小于7,所以ldxxx出题的最大复杂程度为7。
【数据说明】
对于 30%的数据: 2<=n<=20;
对于 60%的数据: 2<=n<=1000;
对于 100%的数据: 2<=n<=100000, 2<=m<=n, 0<=ai<=1000000000。
题解:二分
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5+5; int a[maxn], n, m; bool check(int k){ int now = a[1], cnt = 1, pos1 = 1; while(cnt < m){ int nxt = now+k; int pos = lower_bound(a+pos1, a+1+n+1, nxt) - a; if(pos == n+1)return 0; cnt++; now = a[pos]; pos1 = pos; } return 1; } int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++)scanf("%d", &a[i]); sort(a+1, a+1+n); a[n+1] = 2*a[n]; int lf = 0, rg = a[n], ans; while(lf <= rg){ int mid = (lf + rg) >> 1; if(check(mid))ans = mid, lf = mid + 1; else rg = mid - 1; } printf("%d ", ans); return 0; }
问题 B: 追捕
题目描述
Duan2baka:“jmsyzsfq天下第一蠢!”
jmsyzsfq:“你说什么?!”
于是Duan2baka开始了逃亡的旅程,而jmsyzsfq也开始追捕Duan2baka。 他们来到了一个有n个节点的有向图,迅速逃跑的Duan2baka会首先降落在有向图的某个节点T上,因为怕发出声音,他会永远静止不动。而随后跟来的jmsyzsfq会降落在图上随机一个节点S上(可以与T相同),并可以沿着图中的有向边随意走动。因为jmsyzsfq有着敏锐的嗅觉而且绝顶聪明,他一定会沿着正确的方向寻找Duan2baka。你可以认为,如果图中存在一条合法的从S到T的路径,那么jmsyzsfq一定会抓到Duan2baka——因此jmsyzsfq能否追捕到Duan2baka在他刚刚降落在图上的时候就已经确定了。现在请你帮帮jmsyzsfq,在他降落前告诉他有多大的概率能够追捕到Duan2baka,并告诉他想要追到Duan2baka他可以降落在哪些节点上。
输入
输入第一行包含三个整数n,m,opt,表示图中有n个节点,m条有向边;opt为0或1,含义见输出格式所述。
输入第2至m+1行每行两个整数u,v描述了图中一条从u到v的有向边。
输入第m+2行一个整数T表示Duan2baka降落的位置。
输出
如果输入的opt为0,只需要输出jmsyzsfq能够追捕到Duan2baka的概率。
如果输入的opt为1,输出两行,第一行输出jmsyzsfq能够追捕到Duan2baka的概率;第二行按从小到大输出想要追到Duan2baka他可以降落的节点编号,编号间用空格隔开。
对于jmsyzsfq能够追捕到Duan2baka的概率,是一个既约分数,形如“a/b”(a,b为整数),使gcd(a,b)=1,如果a=b,输出1/1。
样例输入
【样例1输入】
9 10 1
1 2
2 1
2 4
6 1
9 6
6 5
5 3
3 7
3 1
1 8
1
【样例2输入】
5 7 1
1 2
1 3
1 5
2 4
4 1
4 5
5 3
1
样例输出
【样例1输出】
2/3
1 2 3 5 6 9
【样例1解释】
图中共9个节点,能够到达节点S=1的节点共6个:{1,2,3,5,6,9}。所以能够追捕到Duan2baka的概率为6/9=2/3
【样例2输出】
3/5
1 2 4
提示
opt=0和opt=1的数据各50%。
对于opt=0和opt=1的情况,有着完全相同的子任务:
对于10%的数据,n,m≤100。
对于另外30%的数据,n,m≤100,000。
对于另外10%的数据,保证图是一个完全图,即对于任意两个点u和v,必然有两条有向边u→v和v→u。
对于另外20%的数据,保证图是一个有向无环图,即对于任意一个点x,不存在图上一条路径从x经过其它点后回到x。
对于另外20%的数据,保证如果图上存在一条边u→v,一定存在一条边v→u。
对于100%的数据,n,m≤1,000,000,保证数据合法且不存在重边、自环。
题解:简单bfs
#include <bits/stdc++.h> using namespace std; const int maxn = 1e6+5; int n, m, opt, st, cnt = 1, tot, h[maxn]; bool vis[maxn]; int gcd(int a, int b){ return b ? gcd(b, a%b) : a; } struct edge{int v,nxt;}G[maxn]; void add(int u, int v){ G[++tot].v = v; G[tot].nxt = h[u]; h[u] = tot; } void bfs(){ queue <int> Q; Q.push(st); vis[st] = 1; while(!Q.empty()){ int u = Q.front(); Q.pop(); for(int i = h[u]; i; i = G[i].nxt){ int v = G[i].v; if(!vis[v]){ Q.push(v); vis[v] = 1; cnt++; } } } } int main() { scanf("%d%d%d", &n, &m, &opt); for(int i = 1; i <= m; i++){ int u, v; scanf("%d%d", &u, &v); add(v, u); } scanf("%d", &st); bfs(); int d = gcd(cnt, n); int p = cnt/d, q = n/d; printf("%d/%d ",p,q); if(opt)for(int i = 1; i <= n; i++) if(vis[i])printf("%d ",i); }
问题 C: 空间活动
题目描述
贝茜和佩奇正在玩一款游戏,在游戏开始会生成一个有n个点m条单向边的地图,经过每条边需要花费价格为Hi的费用(Hi<=1000)。但是如果两个点可以互相到达,那么这两个点之间需要花费的价格就变为0。总共有k关,每一关会给你两个点a,b(0< a,b <=n),让你求从a走到b的最小花费。
输入
第一行三个数n,m,k;
接下来m行每行三个数,x,y,Hi表示存在一条(x,y)的单向边,边权为Hi;
接下来k行每行两个数a,b。
输出
k行,每行一个数表示从a走到b的最小花费(如果不能从a走到b,那么输出“No”)。
样例输入
4 5 5
1 2 5
2 1 10
3 4 8
4 3 7
2 3 6
1 2
1 3
1 4
4 3
4 1
样例输出
0
6
6
0
No
提示
对于30%的数据n<=20,k<=1;
对于另外30%的数据 n<=100,m=n-1,k<=50;
对于100%的数据 n<=1000,m<=2500,k<=100;
题解:tarjan裸题
#include <bits/stdc++.h> using namespace std; const int maxn = 3000+5; #define inf 1000000008 int n, m, st, ed, k, tot, h[maxn], hh[maxn], tot1, dis[maxn], ans[maxn*3]; bool vis[maxn], run[maxn], inq[maxn], ins[maxn]; struct edge{int v, nxt, w;}G[maxn], g[maxn]; void add(int u, int v, int w){ G[++tot].v = v; G[tot].nxt = h[u]; h[u] = tot; G[tot].w = w; } void add1(int u, int v, int w){ g[++tot1].v = v; g[tot1].nxt = hh[u]; hh[u] = tot1; g[tot1].w = w; } bool spfa(){ queue <int> Q; memset(dis, 127, sizeof(dis)); memset(inq, 0, sizeof(inq)); Q.push(st); inq[st] = 1; dis[st] = 0; while(!Q.empty()){ int u = Q.front(); Q.pop();inq[u] = 0; for(int i = h[u]; i; i = G[i].nxt){ int v = G[i].v; if(dis[v] > dis[u] + G[i].w){ dis[v] = dis[u] + G[i].w; if(!inq[v]) Q.push(v),inq[v] = 1; } } } return dis[ed] < inf; } int dfn[maxn], low[maxn], place[maxn], scc, idx; stack <int> t; void tarjan(int u){ dfn[u] = low[u] = ++idx; ins[u] = 1; t.push(u); for(int i = hh[u]; i; i = g[i].nxt){ int v = g[i].v; if(!dfn[v]){ tarjan(v); low[u] = min(low[u], low[v]); } else if(ins[v]) low[u] = min(low[u], dfn[v]); } if(low[u] == dfn[u]){ scc++; while(1){ int tt = t.top(); t.pop(); place[tt] = scc; ins[tt] = 0; if(tt == u)break; } } } int main() { scanf("%d%d%d", &n, &m, &k); for(int i = 1; i <= m; i++){ int u, v, w; scanf("%d%d%d", &u, &v, &w); add1(u, v, w); } for(int i = 1; i <= n; i++) if(!dfn[i])tarjan(i); for(int i = 1; i <= n; i++) for(int j = hh[i]; j; j = g[j].nxt){ int v = g[j].v; if(place[v] != place[i]) add(place[i], place[v], g[j].w); } for(int i = 1; i <= k; i++){ scanf("%d%d", &st, &ed); st = place[st], ed = place[ed]; if(spfa())printf("%d ", dis[ed]); else printf("No "); } }
今天本来是欢乐ak赛,但空间开小了,数据范围啊