2022牛客暑期多校训练营2
比赛链接
2022牛客暑期多校训练营2
D. Link with Game Glitch
给定 \(m\) 个物品合成的方式,求一个最大的合成损耗参数 \(w\) ,使得所有
物品都无法通过无限合成的方式无限获得
解题思路
spfa,二分
显然,如果 \(w\) 越小,其生成的材料越少,其越能满足题目要求,故可二分 \(w\),一个点如果经过一个环后其参数反而变大,则其一定不满足要求,故可用 \(spfa\) 找最长路的同时判断环,另外由于乘积容易爆 \(double\),需要对路径取对数,即将乘法转换为加法
设二分的循环次数为 \(k\),则:
- 时间复杂度:\((knm)\)
代码
// Problem: Link with Game Glitch
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/33187/D
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=2005;
int n,m,cnt[N];
double d[N];
vector<pair<int,double>> adj[N];
bool v[N];
bool ck(double w)
{
memset(cnt,0,sizeof cnt);
memset(v,0,sizeof v);
memset(d,0,sizeof d);
queue<int> q;
for(int i=1;i<=n;i++)q.push(i),v[i]=true;
while(q.size())
{
int x=q.front();
q.pop();
v[x]=false;
for(auto &t:adj[x])
{
int y=t.fi;
double ww=t.se;
if(d[y]<d[x]+ww+w)
{
cnt[y]=cnt[x]+1;
if(cnt[y]>n)return false;
d[y]=d[x]+ww+w;
if(!v[y])q.push(y),v[y]=true;
}
}
}
return true;
}
int main()
{
help;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
double a,c;
int b,d;
cin>>a>>b>>c>>d;
adj[b].pb({d,(double)log(c/a)});
}
double l=0,r=1;
for(int i=1;i<=100;i++)
{
double mid=(l+r)/2;
if(ck(log(mid)))l=mid;
else
r=mid;
}
cout<<l;
return 0;
}
K. Link with Bracket Sequence I
已知括号序列 \(a\) 是一个长度为 \(m\) 的合法括号序列 \(b\) 的子序列,求可能
的序列 \(b\) 的数量
解题思路
dp
- 状态表示:\(f[i][j][k]\) 表示 \(b\) 的前 \(i\) 个字符,用了 \(a\) 的前 \(j\) 个字符,且左括号比右括号多 \(k\) 个的方案数
状态转移:当母序列 \(i\) 位表示左括号时,如果子序列 \(j\) 位为左括号,则其一定要匹配上,\(\color{red}{为什么?}\)例如母序列为()()
,子序列为()
,则此时母序列表示前面匹配所有子序列的一种情况,即 ()**
的一种情况,如果遇到一定要匹配的反而不匹配则会有重复情况,例如对于()**
和(*)*
两种情况,如果前面匹配了即第二个不会出现())*
的情况,达到了去重的要求
- 时间复杂度:\(O(m^2n)\)
代码
// Problem: Link with Bracket Sequence I
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/33187/K
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=205,mod=1e9+7;
int t,n,m,f[N][N][N];
char s[N];
int main()
{
help;
for(cin>>t;t;t--)
{
cin>>n>>m>>(s+1);
for(int i=0;i<=m;i++)
for(int j=0;j<=n;j++)
for(int k=0;k<=m;k++)f[i][j][k]=0;
f[0][0][0]=1;
for(int i=1;i<=m;i++)
for(int j=0;j<=min(i,n);j++)
for(int k=0;k<=i;k++)
{
int &t=f[i][j][k];
if(j&&s[j]==')')t=(t+f[i-1][j-1][k+1])%mod;
else
t=(t+f[i-1][j][k+1])%mod;
if(k)
{
if(j&&s[j]=='(')t=(t+f[i-1][j-1][k-1])%mod;
else
t=(t+f[i-1][j][k-1])%mod;
}
}
cout<<f[m][n][0]<<'\n';
}
return 0;
}