“蔚来杯“2022牛客暑期多校训练营1

"蔚来杯"2022牛客暑期多校训练营1

C Grab the Seat!


D Mocha and Railgun

题意:

给定一个圆环,中心为(0, 0)。给定T个查询,每次给定环的半径及点Q(xq, yq)及以Q为终点的线段AB的半长为d。求环上最多删除的圆弧长度。
环上某点p可被删除的条件:

  1. p的投影在AB上
  2. A B ⃗ ∗ A P ⃗ > 0 \vec{AB} * \vec{AP} > 0 AB AP >0

思路:

弧长l = 弧度 * r。
要想l最大,那么弧度最大。
可画图模拟。
除此之外需要知道弧度。
acos(), asin()函数可直接得到相应边的夹角的弧度值。

code:

typedef pair <int, int> PII;
const double PI= acos(-1.0);
int main()
{
	int T;
	scanf("%d", &T);
	while(T --)
	{
		double r;
		scanf("%lf", &r);
		double xq, yq, d;
		scanf("%lf %lf %lf", &xq, &yq, &d);
		double ans = 0;
		double cq = sqrt(xq * xq + yq * yq);
		if(xq * xq + yq * yq >= d * d)
		{	
			double cb = cq + d;
			double ca = cq - d;
			double t1 = acos(cb / r);
			double t2 = acos(ca / r);
			ans = max(ans, (t2 - t1) * r);
		}
		else
		{
			double ca = d - cq;
			double cb = cq + d;
			double t1 = asin(ca / r);
			double t2 = asin(cb / r);
			ans = max(ans, (t1 + t2) * r);
		}
		printf("%.9lf\n", ans);
	}
	return 0;
}

I Chiitoitsu

题意:

给定34种形状,每种形状含有4张牌。整个过程只有一名玩家。在初始时会得到13张牌。(保证存在 <= 2张形状一样的牌)在每一轮, 执行以下步骤。

  1. 先从牌堆中摸一张牌
  2. 如果此时满足了7对不同的牌。那么直接获胜。
  3. 此时拥有14张牌,需要弃牌且弃牌不再放入牌堆中。

求期望多少轮可以获胜。

思路:

典型的期望DP。
期望DP往往采取倒推。(写法简单,如果正着推,还额外需要维护到当前点的概率,利用加权求和。此外题目往往起点唯一,到达起点的概率和一定为1,终点不唯一。那么起点的ans即为整体的ans。)
在这里插入图片描述
由于所求为期望轮数。可以把其抽象成一个有向无环图。边权为1。
由于考虑倒着推。
$f[i][j]: 牌堆中还剩下i张牌,且有j张单牌的集合。
每一轮摸牌存在两种情况。

  1. 摸牌为无用牌,直接弃掉。 概率p1 = i − 3 ∗ j i \frac{i - 3 * j}{i} ii3j, f [ i ] [ j ] = p 1 ∗ ( f [ i − 1 ] [ j ] + 1 ) f[i][j] = p1 * (f[i - 1][j] + 1) f[i][j]=p1(f[i1][j]+1)
  2. 摸牌为有用牌可以和之前的牌配对。且此时未满足获胜条件,然后再从牌堆中选一张单牌弃掉。概率p2 = 3 ∗ j i \frac{3 * j}{i} i3j, f [ i ] [ j ] = p 2 ∗ ( f [ i − 1 ] [ j − 2 ] + 1 ) f[i][j] = p2 * (f[i - 1][j - 2] + 1) f[i][j]=p2(f[i1][j2]+1)
  3. 摸牌为有用牌可以和之前的牌配对。且此时满足获胜条件 概率p2 = 3 ∗ j i \frac{3 * j}{i} i3j, f [ i ] [ j ] = p 2 ∗ ( f [ i − 1 ] [ j − 1 ] + 1 ) f[i][j] = p2 * (f[i - 1][j - 1] + 1) f[i][j]=p2(f[i1][j1]+1)

i f ( i − 1 > = 0    A N D    j > = 2 )    f [ i ] [ j ] = p 1 ∗ ( f [ i − 1 ] [ j ] + 1 ) + p 2 ∗ ( f [ i − 1 ] [ j − 2 ] + 1 ) if(i - 1 >= 0 \;AND \;j >= 2) \;f[i][j] = p1 * (f[i - 1][j] + 1) + p2 * (f[i - 1][j - 2] + 1) if(i1>=0ANDj>=2)f[i][j]=p1(f[i1][j]+1)+p2(f[i1][j2]+1)
i f ( i − 1 > = 0    A N D    j = = 1 )    f [ i ] [ j ] = p 1 ∗ ( f [ i − 1 ] [ j ] + 1 ) + p 2 ∗ ( f [ i − 1 ] [ j − 1 ] + 1 ) if(i - 1 >= 0 \;AND \;j == 1) \;f[i][j] = p1 * (f[i - 1][j] + 1) + p2 * (f[i - 1][j - 1] + 1) if(i1>=0ANDj==1)f[i][j]=p1(f[i1][j]+1)+p2(f[i1][j1]+1)
倒着推,由于DFS特性。期望DP往往采用记忆化搜索。
初始化终点f[0–123][0] = 0

code:

ll f[150][20];
ll get_mod0(ll a, ll b)
{
	return (a % mod - b % mod + mod) % mod;
}
ll get_mod1(ll a, ll b)
{
	return (a % mod + b % mod) % mod;
}
ll get_mod2(ll a, ll b)
{
	return (a % mod * (b % mod)) % mod;
}
ll ksm(ll base, ll power)
{
	ll res = 1;
	while(power)
	{
		if(power & 1)
		res = get_mod2(res, base);
		base = get_mod2(base, base);
		power >>= 1; 
	}
	return res;
}
ll dfs(int i, int j) //  123*13
{	
    if(j == 0) return 0;
	if(f[i][j] >= 0) return f[i][j];
	f[i][j] = 0;
	if(i - 1 >= 0 && j >= 2) f[i][j] = (get_mod1(get_mod2(get_mod2(3ll * j, ksm(i, mod - 2)), dfs(i - 1, j - 2)), get_mod2(get_mod2(get_mod0(i * 1ll, 3ll * j), ksm(i, mod - 2)), dfs(i - 1, j))) + 1) % mod;
	if(i - 1 >= 0 && j == 1) f[i][j] = (get_mod2(get_mod2(get_mod0(i * 1ll, 3ll * j), ksm(i * 1ll, mod - 2)), dfs(i - 1, j)) + 1) % mod;
	return f[i][j];
}
int main()
{
	int T;
	scanf("%d", &T);
	int cnt = 0;
	for(int j = 1 ; j <= 4 ; j ++)
	{
		for(int i = 1 ; i <= 9 ; i ++)
		{
			if(j == 4 && i > 7) continue;
			string s;
			s += to_string(i);
			if(j == 1)
			s += "m";
			else if(j == 2)
			s += "p";
			else if(j == 3)
			s += "s";
			else s += "z";
			mp[s] = ++ cnt;
		}
	}
	for(int i = 0 ; i <= 123 ; i ++)
	{
		for(int j = 0 ; j <= 13 ; j ++)
		f[i][j] = -1;
	}
	int t = 0;
	while(T --)
	{
		string str;
		cin >> str;
		int len = str.length();
		vector <int> alls;
		int c = 0;
		for(int i = 0 ; i < len ; i += 2)
		{
			int t = mp[str.substr(i, 2)];
			book[t] ++;
			if(book[t] == 2)
			c ++;
			alls.push_back(t);
		}
		for(auto t : alls)
		book[t] = 0;
		printf("Case #%d: %lld\n", ++t, dfs(123, 13 - c * 2));
	}
	return 0;
}

J Serval and Essay

题意:

给定n个论点,每个论点i存在k个前提。k个前提都满足才能证明论点i时正确的。可选择一个论点作为基本论点,求正确的论点有多少个。
∑ n < = 2 e 5 ,    ∑ i = 1 i = n k [ i ] < = 5 e 5 \sum n <= 2e5, \; \sum_{i = 1} ^ {i = n} k[i] <= 5e5 n<=2e5,i=1i=nk[i]<=5e5
a [ i ] [ j ]    ! = i , 当 j ! = k 时, a [ i ] [ j ] ! = a [ i ] [ k ] a[i][j]\; != i, 当j != k时,a[i][j] != a[i][k] a[i][j]!=i,j!=k时,a[i][j]!=a[i][k]

思路:

题意说的非常繁琐。
把题目进行转化为:
给定 n 个点和若干条边的有向图且不存在重边和自环,如果一个点的所有入边都染上了 给定n个点和若干条边的有向图且不存在重边和自环,如果一个点的所有入边都染上了 给定n个点和若干条边的有向图且不存在重边和自环,如果一个点的所有入边都染上了
颜色,那么当前点也会被染色。可以随机选定一个点进行染色 , 求最多多少个点被染色 颜色,那么当前点也会被染色。可以随机选定一个点进行染色,求最多多少个点被染色 颜色,那么当前点也会被染色。可以随机选定一个点进行染色,求最多多少个点被染色

可以发现转化题意后非常明了。
在这里插入图片描述

假设被1号点染色的集合为A, 被3号点染色的集合为B,3号点又能被1号点染色。

那么集合B必被集合A包含。

说明全图一定存在若干个集合。 对每个集合取max即为答案。

在这里插入图片描述
如图,1号点可被2号点染色,那么1号点和2号点可看作为一个集合。

那么对于图中4号点,就可看作由1号点2号点组成的集合的一条出边。也可被染色。

相当于把2号点所有出边合并到1号点上。 但是不能把2号点的入边合并到1号点上(违背了题意)

考虑如何进行合并。 可暴力set跑启发式合并即可。对于每个点如果只有一条边指向它,即可被染色。

code:

set <int> from[maxn], to[maxn];
int fa[maxn], siz[maxn];
int find(int x)
{
	if(fa[x] == x) return x;
	else return fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
	int a = find(x), b = find(y);
	if(a == b) return ;
	if(to[a].size() < to[b].size()) swap(a, b); //b合并到a上
	fa[b] = a, siz[a] += siz[b];
    vector <PII> alls;
    for(auto t : to[b])
	{
		to[a].insert(t);
		from[t].erase(b);
		from[t].insert(a);
		if(from[t].size() == 1)
		{
            alls.push_back({t, a});
		}	
	} 
    for(auto t : alls)
        merge(t.first, t.second);
}
int main()
{
	int T;
	scanf("%d", &T);
	int cnt = 0;
	while(T --)
	{
		int n;
		scanf("%d", &n);
		for(int i = 1 ; i <= n ; i ++) 
		fa[i] = i, from[i].clear(), to[i].clear(), siz[i] = 1;
		for(int i = 1 ; i <= n ; i ++)
		{
			int k;
			scanf("%d", &k);
			while(k --)
			{
				int v;
				scanf("%d", &v); // v --> i
				from[i].insert(v);
				to[v].insert(i);
			}	
		} 
		for(int i = 1 ; i <= n ; i ++)
		{
			if(from[i].size() == 1)
			{
				merge(*from[i].begin(), i);
			}
		}
		int res = 0;
		for(int i = 1 ; i <= n ; i ++)
		res = max(res, siz[find(i)]);
		printf("Case #%d: %d\n", ++ cnt, res);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值