2022牛客多校第二场

D. Link with Game Glitch

题意:

a a a b b b 物品可以生产 c c c d d d 物品,询问最大的 w w w,用 a a a b b b 物品可以生产 w c wc wc d d d 物品,物品不能无限生产。

解析:

建图。对于一个环,如果边权积大于一,则可以无限生产。增加 w w w 之后, n n n 结点环的边权积为 d 1 d 2 . . . d n × w n d_1d_2...d_n\times w^n d1d2...dn×wn。取 l o g log log,判断边权和是否大于零,然后取相反数,判断边权和是否为负。
对于 w w w ,二分答案,找到最大的 w w w 使不存在负环。 c h e c k check check有没有负环。

代码:

#include
using namespace std;
const int maxn = 1e3+10;
const int maxm = 2e3+10;
const double ops = 1e-8;
typedef long long ll;
typedef double db;
int n, m;
int head[maxn], tot;
struct edge{
	int to, nxt;
	db w;
}e[maxm];
void add(int a, int b, db w){
	e[++tot].nxt = head[a];
	e[tot].to = b;
	e[tot].w = w;
	head[a] = tot;
}
double ans = 0;
double dis[maxn];
int vis[maxn], cnt[maxn];
bool check(double x){
	queue<int> q;
	x = -log(x);
	memset(vis, 0, sizeof(vis));
	memset(dis, 0, sizeof(dis));
	memset(cnt, 0, sizeof(cnt));
	for(int i = 1; i <= n; i++)
		q.push(i);
	while(!q.empty()){
		int u = q.front(); q.pop();
		vis[u] = 0;
		for(int i = head[u]; i; i = e[i].nxt){
			int v = e[i].to;
			db w = e[i].w;
			if(dis[v] > dis[u]+w+x){
				dis[v] = dis[u]+w+x;
				cnt[v] = cnt[u]+1;
				if(cnt[v] > n)
					return true;
				if(!vis[v]){
					q.push(v);
					vis[v] = 1;
				}
			}
		}
	}
	return false;
}
int main(){
	ios_base::sync_with_stdio(false), cin.tie(0);
	cout << fixed << setprecision(12);
	cin >> n >> m;
	int a, b, c, d;
	for(int i = 1; i <= m; i++){
		cin >> a >> b >> c >> d;
		add(b, d, -log(1.0*c/a));
	}
	double l = 0, r = 1;
	for(int i = 1; i <= 100; i++){
		double mid = (l+r)/2;
		if(check(mid)){
			r = mid;
			ans = mid;
		}
		else
			l = mid;
	}
	cout << ans;
}


G. Link with Monotonic Subsequence

题意:

构造 n n n 的排列,使 m a x ( l i s , l d s ) max(lis,lds) max(lis,lds) 最小

解析:

Dilworth定理:对偏序集< A A A ≤ ≤ >,设 A A A 中最长链的长度是 n n n ,则将 A A A 中元素分成不相交的反链,反链个数至少是 n n n
对于本题,设 l i s lis lis 长度为 k k k,则 c n t ( l d s ) ≥ k cnt(lds) \ge k cnt(lds)k ,则 m a x ( l i s , l d s ) ≥ m a x ( k , n k ) ≥ n max(lis,lds) \ge max(k,\frac{n}{k}) \ge \sqrt n max(lis,lds)max(k,kn)n
对于 n = 8 n = 8 n=8 ,排列为:6 7 8 3 4 5 1 2

代码:

#include
using namespace std;
int k, n;
void solve(){
	cin >> n;
    k = ceil(sqrt(n));
    for(int i = n; i >= 1; i -= k)
        for(int j = max(1,i-k+1); j <= i; j++) 
			printf("%d ",j);
    printf("\n");
}
int main(){
    int T;
    cin >> T;
    while(T--)
    	solve();
	return 0;
}


J. Link with Arithmetic Progression

题意:

给定若干点,求回归直线。

解析:

可以直接根据最小二乘法的公式求出回归直线,计算代价。
也可以三分斜率,对于每个斜率,计算出最优的 d d d ,然后计算代价。(直线是 y = k x + d y = kx+d y=kx+d

#include
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
const int maxn = 1e6+10;
const ld eps = 1e-10;
int n;
ld a[maxn], tmp[maxn];
ld check(ld k){
	ld sum = 0, res = 0;
    for(int i = 1; i <= n; i++){
        tmp[i] = a[i] - k*i;
        sum += tmp[i];
    }
    sum /= n;
    for(int i = 1; i <= n; i++){
        res += (tmp[i]-sum) * (tmp[i]-sum);
    }
    return res;
}
void solve(){
	cin >> n;
	for(int i = 1; i <= n; i++)
		cin >> a[i];
	ld l = -1e9, r = 1e9;
	while(r-l >= eps){
		ld m1 = l+(r-l)/3.0;
		ld m2 = r-(r-l)/3.0;
		if(check(m1) > check(m2))
			l = m1;
		else
			r = m2;
	}
	cout << fixed << setprecision(15) << check(l) << endl;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t=1;
    cin>>t;
    while(t--) solve();
}


K. Link with Bracket Sequence I

题意:

给定长度为 n n n 的括号序列,扩展成为长度为 m m m的合法括号序列的方案数。

解析:

考虑 d p dp dp f i , j , k f_{i,j,k} fi,j,k表示在 B B B 的前 i i i 位中,与 A A A l c s lcs lcs j j j 且左括号比右括号多 k k k 个的方案数。

#include
#include
using namespace std;
typedef long long ll;
const int maxn = 2e2+10;
const int mod = 1e9+7;
int f[maxn][maxn][maxn];
char s[maxn];
int n, m;
void solve(){
	memset(f, 0, sizeof(f));
	f[0][0][0] = 1;
	cin >> n >> m >> s+1;
	for(int i = 1; i <= m; i++){
		for(int j = 1; j <= min(i, n); j++){
			for(int k = 1; k <= i; k++){
				if(s[j] == '('){
					f[i][j][k] += f[i-1][j-1][k-1];
					f[i][j][k] %= mod;
					f[i][j-1][k-1] += f[i-1][j-1][k];
					f[i][j-1][k-1] %= mod;
				}
				else{
					f[i][j][k-1] += f[i-1][j-1][k];
                    f[i][j][k-1] %= mod;
                    f[i][j-1][k] += f[i-1][j-1][k-1];
                    f[i][j-1][k] %= mod;
				}
			}
		}
		if(i > n){
			for(int k=1;k<=m;k++){
                f[i][n][k] += f[i-1][n][k-1];
                f[i][n][k] %= mod;
                f[i][n][k-1] += f[i-1][n][k];
                f[i][n][k-1] %= mod;
            }
		}
	}
	cout << f[m][n][0] << endl;
}
int main(){
	ios::sync_with_stdio(false);
    cin.tie(0);
	int T;
	cin>>T;
	while(T--)
		solve();
	return 0;
}

L. Link with Level Editor I

题意:

n n n 个世界,每个世界都是有向图。对于世界中的每个点 u u u,可以直接到达下一个世界的点 u u u,也可以在当前世界中由边 ( u , v ) (u,v) (u,v) 到点 v v v 。起点为 1 1 1,到达 m m m 点胜利。
询问能够胜利的最少连续世界数。

解析:

f i , j f_{i,j} fi,j 为能够到世界 i i i 中的点 j j j 的最晚出发点。空间不够,但 f i , j f_{i,j} fi,j 一定是由 f i − 1 , k f_{i-1,k} fi1,k 即上个世界转移而来,可以滚掉一维。

代码:

#include
#include
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
const int INF = 0x3f3f3f3f;
int f[2][maxn];
int n, m;
int ans = INF;
int main(){
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n >> m;
	memset(f, -INF, sizeof(f));
	for(int i = 1; i <= n; i++){
		int num = 0;
		cin >> num;
		f[(i-1)&1][1] = i;
		for(int j = 1; j <= m; j++)
			f[i&1][j] = f[(i-1)&1][j];//v可以由上个世界v 
		for(int j = 1, u, v; j <= num; j++){
			cin >> u >> v;//v可以由上个世界u
			f[i&1][v] = max(f[i&1][v], f[(i-1)&1][u]);
			if(v == m)
				if(f[i&1][v] != -INF)
					ans = min(ans, i-f[i&1][v]+1); 
		}
	}
	if(ans > n)
		cout << -1 << endl;
	else
		cout << ans << endl;
}

你可能感兴趣的:(算法,c++,图论)