https://vjudge.net/contest/198762#problem
总体难度还行;
A、C、E、F、J稍微有点变形,剩下的都是模板题;
如果n < 100 优先考虑Floyd;
题解
A:
注意到n最多100,直接枚举 “可以交易的地位” 的范围就行了;
做了很长时间,刚开始没读懂题意,后来考虑bfs,状态太多T掉了;
简单粗暴,直接暴力枚举就行了;
B:
裸的FLoyd
C:
好题,本质是个bfs,利用堆(dijkstra的贪心思想)来存状态,把金币超支的状态扔掉;
第一次到达n点时,即为n在金币数量限制下的最短路;
D:
把货币看作点建图;在无向图中求正环,只要回到原点金币增加输出YES;
E:
让两点之间经过的所有路径的最大权值最小;
最大值最小化,显然可以用二分最大值,然后dfs判断两点是否联通;时间复杂度为nlogn
考虑到n <= 200,因此考虑n ^ 3 的floy判断图的连通性即可,代码要简单很多;
F:
可以看到当一个节点的祖先的数量 + 孩子的数量 + 自己 = n时,这个点的排名可以确定;
因为祖先都能打败他,而孩子都能被他打败;所以统计数量就可以了;
可以直接dfs统计,但这样代码太长;
因此,n <= 100 的复杂度提醒我们,这又是一个floyd的变形;
直接判断图的连通性即可。
G、H:
裸的kruskal求最小生成树;
I:
并查集
J:
好题,分别求一遍最小生成树和最大生成树,如果在他们之间有斐波那契数,就可以;
证明:对于两棵树,每棵树的任取一点,两点相连形成新树,那么把一个黑边切掉,形成两个新树,再将白边连起来即可;
如果切掉每一条黑边都不能找到一个白边,则此时的树是最大生成树;
代码
A:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int MAXN = 400001;
int fst[MAXN],nxt[MAXN],di[MAXN],a[MAXN];
ll dis[MAXN];
struct hh {ll d,num;};
struct sh {int f,t,c;}ma[MAXN << 1];
int tot,n,M,T,st,se;
ll ans,inf = 1e18;
priority_queue<hh>q;
bool operator < (hh a,hh b)
{
return a.d > b.d;
}
void build(int f,int t,int c)
{
ma[++ tot].f = f;
ma[tot].t = t;
ma[tot].c = c;
nxt[tot] = fst[f];
fst[f] = tot;
return;
}
bool pd(int x,int y)
{
return abs(x - y) <= M;
}
void Dijkstra(int sx)
{
for(int i = 1;i <= n;i ++) dis[i] = inf;
dis[1] = 0;
q.push((hh){0,1});
while(!q.empty())
{
hh x = q.top();
q.pop();
for(int i = fst[x.num];i;i = nxt[i])
{
int v = ma[i].t;
if(dis[v] > dis[x.num] + (ll)ma[i].c && pd(di[v],st) && pd(di[v],se))
{
dis[v] = dis[x.num] + (ll)ma[i].c;
q.push((hh){dis[v],(ll)v});
}
}
}
return;
}
void solve()
{
cin >> M >> n;
for(int i = 1;i <= n;i ++)
{
cin >> a[i] >> di[i] >> T;
while(T --)
{
int t,c;
cin >> t >> c;
build(i,t,c);
}
}
ans = a[1];
int i = (di[1] - M >= 1 )? di[1] - M : 1;
for(st = i;st <= di[1];st ++)
{
se = st + M;
Dijkstra(1);
for(int j = 1;j <= n;j ++) ans = min(ans,dis[j] + (ll)a[j]);
}
cout << ans << endl;
return;
}
int main()
{
solve();
return 0;
}
C:
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
int fst[20001],n,m,C,f,t,d,c;
struct hh{int f,t,c,d,nxt;}ma[20002];
struct sh{int dis,D,num;}x;
priority_queue<sh>q;
bool operator < (sh a,sh b) {return a.dis == b.dis ? a.D > b.D : a.dis > b.dis;}
int bfs(){
q.push((sh){0,0,1});
while(!q.empty()){
x = q.top(),q.pop();
if(x.num == n) return x.dis;
for(int i = fst[x.num];i;i = ma[i].nxt)
if(x.D + ma[i].d <= C)
q.push((sh){x.dis + ma[i].c,x.D + ma[i].d,ma[i].t});
}
return -1;
}
int main(){
cin >> C >> n >> m;
for(int i = 1;i <= m; i ++){
scanf("%d%d%d%d",&f,&t,&c,&d);
ma[i] = (hh){f,t,c,d,fst[f]},fst[f] = i;
}
printf("%d\n",bfs());
return 0;
}
J:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 500001;
int n,m,T,tt;
int fa[MAXN],f[MAXN];
struct hh{
int f,t,c;
}ma[MAXN];
int find(int x){
int r = x,t;
while(r != fa[r]) r = fa[r];
while(x != r) t = fa[x],fa[x] = r,x = t;
return r;
}
bool cmp(hh a,hh b){
return a.c < b.c;
}
void solve(){
int ans1 = 0,ans2 = 0;
cin >> n >> m;
for(int i = 1;i <= m;i ++) scanf("%d%d%d",&ma[i].f,&ma[i].t,&ma[i].c);
sort(ma + 1,ma + m + 1,cmp);
for(int i = 1;i <= n;i ++) fa[i] = i;
for(int i = 1;i <= m;i ++){
int fx = find(ma[i].f),fy = find(ma[i].t);
if(fx != fy){
fa[fx] = fy;
ans1 += ma[i].c;
}
}
int flag = 0;
for(int i = 1;i <= n;i ++){
if(fa[i] == i) flag ++;
fa[i] = i;
}
for(int i = m;i >= 1;i --){
int fx = find(ma[i].f),fy = find(ma[i].t);
if(fx != fy){
fa[fx] = fy;
ans2 += ma[i].c;
}
}
int pos = lower_bound(f,f + 31 ,ans1) - f;
if(f[pos] <= ans2 && flag == 1) printf("Case #%d: Yes\n",++ tt);
else printf("Case #%d: No\n",++ tt);
return;
}
int main(){
f[0] = f[1] = 1;
for(int i = 2;i <= 30;i ++) f[i] = f[i - 1] + f[i - 2];
cin >> T;
while(T --) solve();
return 0;
}