“范式杯”2023牛客暑期多校训练营1 DKJH

D.Chocolate

 “范式杯”2023牛客暑期多校训练营1 DKJH_第1张图片

 结论:除了1*1的情况,先手必胜。

思路:对于1*n的情况下先手都是必胜,n*m的情况先手总可以让后手先造成1*m或1*n的局面。

#include
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'

using namespace std;

typedef pair PII;
typedef long long ll;

int main()
{
	IOS
	ll n, m;
	cin >> n >> m;
	if(n == 1 && m == 1)
	{
		cout << "Walk Alone";
	}
	else cout << "Kelin";
	
	return 0;
}

K.Subdivision

 “范式杯”2023牛客暑期多校训练营1 DKJH_第2张图片

题意:给一个图,边权都为1,可以在点与点的边上新加一个点,求距离节点1不超过k的点最多可以有多少个。

思路:对于一条路径1-2-3-4-5,与其在开始或者中间加点,不如在最后一个点与前一个点的边上加点,这样对后继节点造成的影响是最小的。

可以建一颗树,共n-1条边来作为支撑,除了叶子结点和其前驱节点连成的边,其他树边上是不建点的。对于非树边,在两边的节点上加点是不会对其他节点造成任何影响的,非常的nice。

 #include
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'

using namespace std;

typedef pair PII;
typedef long long ll;

const int N = 100010, M = 400010;

int k;
int h[N], e[M], ne[M], idx;
bool st[N];
int p[N], dist[N];//p数组记录前驱节点
stack Q;
ll ans = 1;

void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

void bfs()
{
	//st[1] = true;
	queue q;
	q.push(1);
	memset(dist, 0x3f, sizeof dist);
	dist[1] = 0;
	
	while(q.size())
	{
		int t = q.front();
		q.pop();
		
		for(int i = h[t]; i != -1; i = ne[i])
		{
			int j = e[i];
			if(dist[j] > dist[t] + 1)
			{
				dist[j] = dist[t] + 1;
				q.push(j);
				Q.push(j);
				p[j] = t;
			}
		}
	}
}

void bfs2()
{
	while(Q.size())
	{
		int t = Q.top();
		Q.pop();
		if(dist[t] > k)continue;
		ans ++;
		
		int cnt =0;
		for(int i = h[t]; i != -1; i = ne[i])
		{
			cnt ++; 
		}
		
		if(cnt == 1)
		{
			ll tmp = k - dist[t];
			ans += tmp;
			continue;
		}
		
		for(int i = h[t]; i != -1; i = ne[i])
		{
			int j = e[i];
			if(dist[j] > k)continue;
			if(p[t] == j || p[j] == t)continue;
			
			ll tmp = k - dist[j];
			ans += tmp;
		}
	}
}

int main()
{
	IOS
	int n, m;
	cin >> n >> m >> k;
	memset(h, -1, sizeof h);
	
	for(int i = 0; i < m; i ++)
	{
		int a, b;
		cin >> a >> b;
		add(a, b), add(b, a);
	}
	
	bfs();
	
	bfs2();
	
	cout << ans;
	
	return 0;
}

J. Roulette

“范式杯”2023牛客暑期多校训练营1 DKJH_第3张图片

 赢一次后押1块,输一次赌注加倍。

赢:1 = 1

输赢:-1 + 2 = 1

输输赢:-1 - 2 + 4 = 1

输输输赢:-1 -2 -4 + 8 = 1

由等比数列可以发现输输输输...赢造成的结果是和赢造成结果是一样的,都可以看作是在当前步骤赢,当前步骤输掉的概率就是(1 - 输到破产的概率)

连输多少次才能破产的次数:r = log2(n + 1)

在区间2^{r} - 1 ~ 2^{r + 1} - 2范围内的连输多少次才能破产的次数是相等的,都是r,共计2的r次方个数。

还有逆元可以直接当分数用。

#include
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'

using namespace std;

typedef pair PII;
typedef long long ll;

const int mod = 998244353;

ll qmi(ll a, ll k)
{
	ll res = 1, p = mod;
	while(k)
	{
		if(k & 1)res = res * a % p;
		k >>= 1;
		a = a * a % p;
	}
	return res;
}

ll inv(ll x)
{
	return qmi(x, mod - 2);
}

int main()
{
	IOS
	ll n, m;
	cin >> n >> m;
	m += n;
	ll ans = 1, inv2 = inv(2);
	while(n < m)
	{
		ll r = __lg(n + 1);//连输多少次破产 
		ll a = (1 - qmi(inv2, r) + mod) % mod;//概率 
		ll k = min(m, (1ll << r + 1) - 1); //枚举次数的时候不要用快速幂
		ll v = k - n;//次数 
		
		ans = ans * qmi(a, v) % mod;
		n = k;
	}
	cout << ans;
	
	return 0;
}

H.Matches

“范式杯”2023牛客暑期多校训练营1 DKJH_第4张图片

 官方图解:

 

​​​​​​​“范式杯”2023牛客暑期多校训练营1 DKJH_第5张图片

 所有情况排列组合不过6种情况,两两一组,共计3组。

可以看出只有两个区间一正序一反序 且有公共区间的时候交换才是有好处的,减少的代价是公共区间长度的两倍。

可以开一个结构体数组,记录左右区间和正序反序的状态,按左端点从左到大的顺序枚举。

如果当前枚举到的区间是正序的,就从已经枚举到的反序区间里选取最靠右的端点(可以用一个变量记录),如果该端点比当前区间左端点大就说明有重叠区域,为min(tmp,r)- l。

如果当前枚举到的区间是反序的,就从已经枚举到的正序区间里选取最靠右的端点(可以用一个变量记录),如果该端点比当前区间左端点大就说明有重叠区域,为min(tmp,r)- l。

ans - 两倍的最大重叠区域就是答案。

#include
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'

using namespace std;

typedef pair PII;
typedef long long ll;
typedef struct
{
	int l, r, type;
} Node;

const int N = 1000010;

Node q[N];
int a[N], b[N];

bool cmp(Node A, Node B)
{
	return A.l < B.l;
}

int main()
{
	IOS
	int n;
	cin >> n;
	ll ans = 0;
	for(int i = 1; i <= n; i ++)cin >> a[i];
	for(int i = 1; i <= n; i ++)
	{
		cin >> b[i];
		q[i].l = min(a[i], b[i]);
		q[i].r = max(a[i], b[i]);
		q[i].type = (a[i] > b[i]);//1反序 0正序 
		
		ans += abs(a[i] - b[i]);
	}
	
	sort(q + 1, q + 1 + n, cmp);
	
	ll tmp = -5e9, res = 0;
	for(int i = 1; i <= n; i ++)
	{
		if(q[i].type == 0)
		{
			if(tmp <= q[i].l)continue;
			
			ll k = min((ll)q[i].r, tmp);
			res = max(res, k - q[i].l);
		}
		else
		{
			tmp = max(tmp, (ll)q[i].r);
		}
	} 
    
    tmp = -5e9;
    for(int i = 1; i <= n; i ++)
	{
		if(q[i].type == 1)
		{
			if(tmp <= q[i].l)continue;
			
			ll k = min((ll)q[i].r, tmp);
			res = max(res, k - q[i].l);
		}
		else
		{
			tmp = max(tmp, (ll)q[i].r);
		}
	} 
	
	ans -= res * 2;
	
	cout << ans;
	
	return 0;
}

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