牛客题集:练习赛74、75
练习赛74
D
因为要L尽量大,R尽量小,所以我们把所有的边按边长都排好序,用并查集,从大到小一个个加边,直到S和T是联通的为止,从而找出L;重置并查集,再把所有大于L的边加进去,从小到大,直到S和T是联通的为止,找到R。
#include <bits/stdc++.h> #define debug freopen("r.txt","r",stdin) #define mp make_pair #define ri register int #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef double lf; typedef pair<ll, ll> pii; const int maxn = 3e6+10; const int N = 1500+10; const int INF = 0x3f3f3f3f; const int mod = 1e9+7; const int hash_num = 131; const double eps=1e-6; const double PI=acos(-1.0); inline ll read(){ll s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w;} ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;} struct edge { int u,v; ll w; bool operator<(const edge& x) const{ return x.w>w; } }e[maxn]; int n,m,s,t,L,R; struct DSU{ int a[maxn],sz[maxn]; void init(int n){ iota(a,a+n+1,0); fill(sz,sz+n+1,1); } int fa(int x){ return a[x]==x?x:a[x]=fa(a[x]); } bool query(int x,int y){ //查找 return fa(x)==fa(y); } void join(int x,int y){ //合并 x=fa(x),y=fa(y); if(x==y)return; if(sz[x]>sz[y])swap(x,y); a[x]=y; sz[y]+=sz[x]; } int operator[](int x){return fa(x);} }d; int main() { // debug; scanf("%d%d%d%d",&n,&m,&s,&t); for (int i = 1; i <= m; ++i) { scanf("%d%d%lld",&e[i].u,&e[i].v,&e[i].w); } sort(e+1,e+1+m); d.init(n); for (int i = m; i>=1 ; --i) { d.join(e[i].u,e[i].v); if (d.query(s,t)) { L=i; break; } } d.init(n); for (int i = L; i <= m; ++i) { d.join(e[i].u,e[i].v); if (d.query(s,t)) { R=i; break; } } printf("%lld %lld\n",e[L].w,e[R].w); return 0; }
E
跟Floyd最短路结合的期望概率题
因为题目要求黑点的期望个数,那么我们就可以分为对每个点出现黑色的概率之和,已经是黑色的,那就直接+1,不是黑色的,就用Floyd来求经过这个点的最短路有几条,然后就能算出概率。
#include <bits/stdc++.h> #define debug freopen("r.txt","r",stdin) #define mp make_pair #define ri register int #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef double lf; typedef pair<ll, ll> pii; const int maxn = 1e3+10; const int N = 1500+10; const int INF = 0x3f3f3f3f; const int mod = 1023694381; const int hash_num = 131; const double eps=1e-6; const double PI=acos(-1.0); inline ll read(){ll s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w;} ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;} ll n,m,K,f[maxn][maxn],w,now,ans; int a[maxn],u,v; int main() { // debug; memset(f,0x3f,sizeof(f)); n=read(),m=read(),K=read(); for (int i = 1; i <= n; ++i) { a[i]=read(); f[i][i] = 0; } for (int i = 1; i <= m; ++i) { u=read(),v=read(),w=read(); f[u][v]=f[v][u]=min(f[u][v],w); } for (int k = 1; k <= n; ++k) for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) f[i][j]=min(f[i][k]+f[k][j],f[i][j]); for (int k = 1; k <= n; ++k) { if (a[k] == 1) { ans = (ans+1)%mod; continue; } now = 0; ll s = 0; for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) { if (i==j) continue; s++; if (f[i][j]==f[i][k]+f[k][j]) { now++; } } ll p = (s - now) * qpow(s,mod-2) % mod; ans = (ans + 1ll + mod - qpow(p,K))%mod; } cout << ans <<endl; return 0; }
练习赛75
B
对于k=0,完全背包即可
而n=2的情况,交换再完全背包
剩下情况,我们只需要找出价值最大的物品和体积最小的物品进行交换,得到价值最大且体积最小的物品即可。
#include <bits/stdc++.h> #define debug freopen("r.txt","r",stdin) #define mp make_pair #define ri register int #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef double lf; typedef pair<ll, ll> pii; const int maxn = 1e6+10; const int N = 1500+10; const int INF = 0x3f3f3f3f; const int mod = 1e9+7; const int hash_num = 131; const double eps=1e-6; const double PI=acos(-1.0); inline ll read(){ll s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w;} ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;} int a[maxn],b[maxn]; int n,m,k,minn,maxx; ll f[maxn]; int main() { //bug; n=read(),m=read(),k=read(); for (int i = 1; i <= n; ++i) { a[i]=read(); } for (int i = 1; i <= n; ++i) { b[i]=read(); } if (k==0 || n==2) { if (n==2 && k%2==1) swap(b[1],b[2]); for (int i = 1; i <= n; ++i) { for (int j = a[i]; j <=m; j++) f[j] = max(f[j],f[j-a[i]]+b[i]); } cout<< f[m] <<endl; } else { minn = a[1]; maxx = b[1]; for (int i = 2; i <= n; ++i) { minn=min(minn,a[i]); maxx=max(maxx,b[i]); } cout<< 1ll*m/minn*maxx <<endl; } return 0; } /* */
D
可以明显的看出是每次选择两个最小的值进行操作才能到最后最大化答案,但是,因为取模的原因,可能会导致 1e9+8 < 2 的尴尬境地,python做可以不用考虑这种问题,因为可以最后答案取模。
但是C++做,我们就要想到这个问题。
我们把当前选出的值设为 x 和 y
那么一旦 x*y+k 大于当前数列最大值,就说明了之后的情况绝对是 选出两个最小的数,算出结果之后大于数列最大的数,所以我们这时我们可以不用再判断谁大谁小的问题,直接把一开始的有序数列(从小到大),不断进行选前面两个,算出结果放到数列最后面这个重复操作即可。
#include <bits/stdc++.h> #define debug freopen("r.txt","r",stdin) #define mp make_pair #define ri register int #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef double lf; typedef pair<ll, ll> pii; const int maxn = 1e6+10; const int N = 1500+10; const int INF = 0x3f3f3f3f; const int mod = 1e9+7; const int hash_num = 131; const double eps=1e-6; const double PI=acos(-1.0); inline ll read(){ll s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w;} ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;} ll n,k,x,maxx,y; priority_queue<ll, vector<ll>, greater<ll>> q; deque<ll> d; int main(int argc, char const *argv[]) { // debug; n=read(),k=read(); for (int i = 1; i <= n; ++i) { x=read(); maxx=max(maxx,x); q.push(x); } if (n==1) { cout << x <<endl; return 0; } for (int i = 2; i <= n; ++i) { x=q.top(); q.pop(); y=q.top(); q.pop(); if (x*y+k>=maxx) break; else q.push(x*y+k); } while (q.size()) { d.push_back(q.top()); q.pop(); } d.push_back((x*y+k)%mod); while (d.size()>1) { x=d.front(); d.pop_front(); y=d.front(); d.pop_front(); d.push_back((x*y+k)%mod); } cout << d.front() <<endl; return 0; }