首页 > 其他分享 >2022 China Collegiate Programming Contest (CCPC) Mianyang Onsite

2022 China Collegiate Programming Contest (CCPC) Mianyang Onsite

时间:2023-10-07 16:36:41浏览次数:54  
标签:Onsite suf pre frac Contest int ll k1 Mianyang

2022 China Collegiate Programming Contest (CCPC) Mianyang Onsite

C. Catch You Catch Me

解题思路:

站在距离出口最近的点等深度深的蝴蝶飞上来即可。

时间复杂度:\(O(n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

void solve()
{
	int n;
	scanf("%d",&n);
	vector<vector<int>> adj(n + 1);
	for(int i = 1;i<n;i++)
	{
		int a,b;
		scanf("%d %d",&a,&b);
		adj[a].push_back(b);
		adj[b].push_back(a);
	}
	vector<int> p(n + 1),ans(n + 1);
	auto dfs = [&](auto self,int u,int fa) -> int
	{
		p[u] = fa;
		ans[u] = 1;
		for(auto v : adj[u])
		{
			if(v == fa)
			{
				continue;
			}
			ans[u] = max(ans[u],self(self,v,u) + 1);
		}
		return ans[u];
	};
	dfs(dfs,1,-1);
	ll res = 0;
	for(int i = 1;i<=n;i++)
	{
		if(p[i] == 1)
		{
			res += ans[i];
		}
	}
	printf("%lld\n",res);
}

int main()
{
	
	int t = 1;
	while(t --)
	{
		solve();
	}
	return 0;
}

G. Let Them Eat Cake

解题思路:

由于每个数互不相同,所以对于每个数来说,我们和右边一位比较,例如\(i-1,i,i+1\)。

若\(a[i-1] < a[i]<a[i+1]\),则删\(a[i-1],a[i]\).

若\(a[i-1] > a[i] > a[i + 1]\),则删\(a[i],a[i+1]\)。

若\(a[i-1] > a[i] < a[i+1]\),则删\(a[i]\)。

若\(a[i-1] < a[i] > a[i+1]\),则删\(a[i-1],a[i+1]\)。

如上举例,在看4个的情况。

每三个中最少删1个,没四个中最少要删两个。因为三个中有两对,四个中有三对。最多两对答案相同。

所以直接暴力枚举即可

时间复杂度:\(O(nlogn)\)。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;

void solve()
{
	int n;
	scanf("%d",&n);
	vector<int> a(n);
	for(int i = 0;i<n;i++)
	{
		scanf("%d",&a[i]);
	}
	int cnt = 0;
	while(a.size() != 1)
	{
		vector<int> b;
		for(int i = 0;i<n;i++)
		{
			if(i == 0)
			{
				if(a[0] > a[1])
				{
					b.push_back(a[0]);
				}
			}
			else if(i == n - 1)
			{
				if(a[n - 1] > a[n - 2])
				{
					b.push_back(a[n - 1]);
				}
			}
			else
			{
				if(a[i] > a[i-1] && a[i] > a[i+1])
				{
					b.push_back(a[i]);
				}
			}
		}
		cnt ++;
		a = b;
		n = a.size();
	}
	cout<<cnt;
}

int main()
{
	int t = 1;
	while(t--)
	{
		solve();
	}
}

H. Life is Hard and Undecidable, but...

解题思路:

题目看似很长,其实构造简单。

斜着构造一条长度为\(2k - 1\)的链即可。

注意:图中没有活细胞的位置上默认存在死细胞,这也是不能横竖构建链的原因。

时间复杂度:\(O(k)\)

代码:

#include<bits/stdc++.h>
using namespace std;
void solve()
{
	int k;
	scanf("%d",&k);
	int n = 2 * k - 1;
	printf("%d\n",n);
	for(int i= 1;i<=n;i++)
	{
		printf("%d %d\n",i,i);
	}
}

int main()
{
	int t = 1;
	while(t--)
	{
		solve();
	}
}

A. Ban or Pick, What's the Trick

解题思路:

首先,每次选最大的,如果时自己的就\(pick\),如果是对面的就\(ban\)的贪心是错的。

如果我选择\(pick\),那么选自己中当时可选的最大的。如果我选择\(ban\),那么选对面可\(ban\)的种最大的。这个贪心是对的。

\(f[i][k1][k2]\):在第\(i\)论\(bp\),\(A\)方\(pick\)了\(k1\)位英雄,\(B\)方\(pick\)了\(k2\)位英雄,此时\(A\)方的最大领先分数。

注意,轮数的就看判断是哪方进行\(bp\)。本题要求选的是两方按照对自身最优的策略进行\(bp\)。所以从自身角度看对面的正收益对于自身都是负收益。

那么我们可以分为进行\(pick\)和进行\(ban\)两个子集进行转移。

\(r1 = \lfloor\frac {1}{i}\rfloor\),表示\(A\)队已经\(bp\)过的总次数。

\(ban2 = r1 - k1\),表示\(A\)队已经\(ban\)了\(B\)队多少人。

\(p2 = k2 + ban2 + 1\),表示如果从\(B\)队的池子中选人,选第几个。

本题直接转移搜索会\(TLE\),所以使用记忆化搜索来优化时间。

对于当前能否选择记得进行合法性判断。

单纯地\(ban\)对面不会带来直接性收益,只有确实\(pick\)到了,才会有正收益。

时间复杂度:\(O(n * k^2)\)

代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const ll INF = -1e18;
bool cmp(int a,int b)
{
	return a > b;
}

void solve()
{
	int n,k;
	scanf("%d %d",&n,&k);
	vector<int> a(n + 1),b(n + 1);
	for(int i = 1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	} 
	for(int i = 1;i<=n;i++)
	{
		scanf("%d",&b[i]);
	}
	vector<vector<vector<ll>>> f(n + n + 10,vector<vector<ll>>(k + 1,vector<ll>(k + 1,-INF)));
	vector<vector<vector<bool>>> st(2 *n + 10,vector<vector<bool>>(k + 1,vector<bool>(k + 1)));
	sort(a.begin() + 1,a.end(),cmp);
	sort(b.begin() + 1, b.end(),cmp);
	auto dfs = [&](auto self,int i,int k1,int k2) -> ll
	{
		if(i > 2 * n)
		{
			return 0;
		}
		if(st[i][k1][k2])
		{
			return f[i][k1][k2];
		}
		ll &ans = f[i][k1][k2];
		st[i][k1][k2] = true;
		ll r1 = i / 2;
		ll r2 = (i - 1) / 2;
		ll ban1 = r2 - k2;
		ll ban2 = r1 - k1;
		ll p1 = k1 + ban1 + 1;
		ll p2 = k2 + ban2 + 1;
		if((i & 1))
		{
			if(k1 < k && p1 <= n)
			{
				if(p2 <= n)
				{
					ans = max(-self(self,i + 1,k1 + 1,k2) + a[p1],-self(self,i + 1,k1,k2));
				}
				else
				{
					ans = -self(self,i + 1,k1 + 1,k2) + a[p1];
				}
				
			}
			else
			{
				
				ans = -self(self,i + 1,k1,k2);
			}
		}
		else
		{
			if(k2 < k && p2 <= n)
			{
				if(p1 <= n)
				{
					ans = max(-self(self,i + 1,k1,k2 + 1) + b[p2],-self(self,i + 1,k1,k2));
				}
				else
				{
					ans = -self(self,i + 1,k1,k2 + 1) + b[p2];
				}
				
			}
			else
			{
				ans = -self(self,i + 1,k1,k2);
			}
		}
//		cout<<i<<' '<<k1<<' '<<k2<<' '<<ans<<endl;
		return ans;
	};
	printf("%lld",dfs(dfs,1,0,0));
	
}

int main()
{
	int t = 1;
	while(t--)
	{
		solve();
	}
}

M. Rock-Paper-Scissors Pyramid

解题思路:

举例:

\(SPR\)最终答案是\(S\)。

虽然\(S\)打不过\(R\),但如果\(S和R\)之间有一个\(P\),那么,\(P\)后面的所有\(R\)一定碰不到\(S\).

所以,从左往右扫,遇到打不过的就不断出栈知道空或者打得过。

打得过了就入栈。

最终栈底就是胜者。

时间复杂度:\(O(n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

void solve()
{
	string s;
	cin>>s;
	auto battle = [&](char a,char b)
	{
		if(a == 'S' && b == 'P')
		{
			return true;
		}
		if(a == 'P' && b == 'R')
		{
			return true;
		}
		if(a == 'R' && b == 'S')
		{
			return true;
		}
		return false;
	};
	stack<int> q;
	int n = s.size();
	for(int i = 0;i<n;i++)
	{
		while(!q.empty() && !battle(s[q.top()],s[i]))
		{
			q.pop();
		}
		q.push(i);
	}
	char ans;
	while(q.size())
	{
		ans = s[q.top()];
		q.pop();
	}
	printf("%c\n",ans);
}

int main()
{
	int t = 1;
	scanf("%d",&t);
	while(t--)
	{
		solve();
	}
}

D.Gambler's Ruin

解题思路:

我们先对\(p\)进行降序排序。

根据题意不难得知,若\(p_i \cdot x\geq 1\),那么\(p_k \cdot x \geq 1,(k \leq i)\)。对于\(y\)则是反序。

也就是说,对于\(i\)的前缀都会投\(BU\),对于\(j\)的后缀都会投\(BC\).

对应的边界\(x\)即为\(\frac{1}{p_i}\),毕竟如果超出这个边界但又不满足\(p_{i + 1} \cdot x \geq 1\),多出的赔率不就是白送钱吗。

对于\(j\)自然就是\(\frac{1}{1 - p_j}\)。

那么答案就是

\[pre_i +suf_j - max(\frac{pre_i}{p_i},\frac{suf_j}{1-p_j}) \]

我们固定\(i\)。

如果我们将\(j\)向后移,\(p_j\)减小,\(1-p_j\)增大,\(suf_j\)减小,所以\(\frac{suf_j}{1-p_j}\)减小。

如果此时\(\frac{suf_j}{1-p_j}\geq \frac{pre_i}{p_i}\),那么

\[pre_i +suf_j - max(\frac{pre_i}{p_i},\frac{suf_j}{1-p_j}) = pre_i - \frac{p_j}{1 - p_j}suf_j \]

其中,\(\frac{p_j}{1 - p_j}suf_j\),随着\(j\)不断右移而减小。答案就不断增大,直到\(\frac{suf_j}{1-p_j}\leq \frac{pre_i}{p_i}\)。假设这个大小翻转的边界为\(k\),那么答案就在\(j =k和j = k - 1\)之中。根据上面的式子可知,此时\(\frac{p_j}{1 - p_j}suf_j\)来到最小,答案达到最大。

对于\(i\)的右移,\(p_i\)减小,\(pre_i\)增大,\(\frac{pre_i}{p_i}\)增大。所以对应的\(j\)的边界值\(k\)下标也只会往后移。

如果此时\(\frac{suf_j}{1-p_j}\leq \frac{pre_i}{p_i}\),那么

\[pre_i +suf_j - max(\frac{pre_i}{p_i},\frac{suf_j}{1-p_j}) = suf_j - \frac{p_i - 1}{p_i}pre_i \]

若\(j\)不变,答案随\(i\)增大而减小。

这样\(i\)和\(j\)之间就具有单调性了,即随着\(i\)变大,\(j\)的边界也会变大。

双指针求解。

时间复杂度:\(O(n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef pair<double,double> pdd;
#define fi first
#define se second
const double eps = 1e-10;

bool cmp(pdd a,pdd b)
{
	return a.fi > b.fi;
}
void solve()
{
	int n;
	scanf("%d",&n);
	vector<pdd> v(n + 1);
	for(int i = 1;i<=n;i++)
	{
		scanf("%lf %lf",&v[i].fi,&v[i].se);
	}
	sort(v.begin() + 1,v.end(),cmp);
	vector<double> pre(n + 10),suf(n + 10);
	int cnt = 0;
	for(int i = 1;i <= n;i++)
	{
		if(i == 1 || fabs(v[i].fi - v[i-1].fi) > eps)
		{
			v[++cnt] = v[i];
		}
		else
		{
			v[cnt].se += v[i].se;
		}
	}
	n = cnt;
	for(int i = 1;i <= n;i ++)
	{
		pre[i] = pre[i-1] + v[i].se;
	}
	for(int i = n;i; i --)
	{
		suf[i] = suf[i+1] + v[i].se;
	}
	auto get = [&](int i,int j) -> double
	{
		return pre[i] + suf[j] - max(pre[i] / v[i].fi,suf[j] / (1.0 - v[j].fi));
	};
	int j = n;
	double ans = 0;
	for(int i = 1;i<=n;i ++)
	{
		if(i == n || fabs(v[i].fi - v[i + 1].fi) > eps)
		{
			while(j > i && pre[i] / v[i].fi > suf[j] / (1.0 - v[j].fi))
			{
				j --;
			}
			ans = max(ans,get(i,j + 1));
			if(j <= i)
			{
				break;
			}
			ans = max(ans,get(i,j));
		}
	}
	printf("%.12lf",ans);
}

int main()
{
	int t = 1;
	while(t--)
	{
		solve();
	}
}

标签:Onsite,suf,pre,frac,Contest,int,ll,k1,Mianyang
From: https://www.cnblogs.com/value0/p/17746605.html

相关文章

  • AtCoder Grand Contest 057 E RowCol/ColRow Sort
    洛谷传送门AtCoder传送门首先考虑一个经典的套路:转\(01\)。具体而言,我们考虑若值域是\([0,1]\)怎么做。发现可以很容易地判定一个\(A\)是否合法。设矩阵第\(i\)行的和为\(r_i\),第\(j\)列的和为\(c_j\),那么合法当且仅当\(A\)的\(\{r_i\}\)和\(\{c_j\}\)(可重集......
  • 2022 China Collegiate Programming Contest (CCPC) Weihai Site EAJGCI
    2022ChinaCollegiateProgrammingContest(CCPC)WeihaiSite目录2022ChinaCollegiateProgrammingContest(CCPC)WeihaiSiteVP概况E-PythonWillbeFasterthanC++A-DunaiJ-Eat,Sleep,RepeatG-Grade2C-GrassI-DragonBloodlineVP概况这场我一年......
  • 2022-2023 ICPC Central Europe Regional Contest
    The1stUniversalCup.Stage8:SloveniaD.Deforestation这道题出题人比较谜语人,对于一个分叉点,只能选择若干个儿子和父亲组成一组,剩下的儿子之间不能相互组合。所以从叶子节点开始贪心处理就好。对于一个父亲他有若干个儿子,就贪心的选择剩下部分更小的儿子。#include<bits......
  • The 2021 ICPC Asia Macau Regional Contest
    A.SoI'llMaxOutMyConstructiveAlgorithmSkills首先一行正一行反的把所有的行拼到一起,然后判断一下正着走时候合法不合法就反过来走就好。#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongusingvi=vector<int>;voidsolve(){intn;......
  • The 2022 ICPC Asia Jinan Regional Contest
    A.Tower首先用了dp验证出把一个数字变成另一个数字的最优解一定是能除就先进行除法,然后再使用加一减一。这样我们就有\(O(\logn)\)的复杂度求出把一个数变成另一个数的最小代价。然后就是猜测最终的目标一定是某个数除了若干次二得到的。所以就枚举一下目标即可。#include......
  • The 2018 ACM-ICPC Asia Qingdao Regional Contest, Online (The 2nd Universal Cup
    The2018ACM-ICPCAsiaQingdaoRegionalContest,Online(The2ndUniversalCup.Stage1:Qingdao)J-PresstheButton\(1\leqa,b,c,d\leq10^6\)题解容易发现存在循环节,每次位于\(gcd(a,c)\)的倍数的位置所以我们考虑处理一个循环节内的情况如果\(v\le......
  • AtCoder Grand Contest 036 F Square Constraints
    洛谷传送门AtCoder传送门本质是\(p_i\in[l_i,r_i]\)的计数问题。当\(1\lei\len\)时,\(l_i\)才可能不等于\(1\)。考虑容斥,设钦定\(m\)个不满足条件(上界为\(l_i-1\)),其余任意(上界为\(r_i\))。然后按照上界排序后dp,设\(f_{i,j}\)为考虑前\(i\)个元素,已经......
  • 2017 China Collegiate Programming Contest Final (CCPC-Final 2017)
    Preface今天打学校统一要求的这场CCPC2017Final,直接被打爆了,各种数学题搞得人生活不能自理主要是H徐神开场就秒出了正确的思路,然后一心认准高斯消元然后一直想+写+调到结束都没卡过去比赛最后20min的时候祁神想到了更好写的基于施密特正交化的方法,可以碍于时间有限没调出来不......
  • AtCoder Beginner Contest 288 Ex A Nameless Counting Problem
    洛谷传送门AtCoder传送门考虑到规定单调不降比较难搞。先设\(g_t\)为长度为\(t\)的满足条件的序列个数(可重且有顺序)。求这个可以设个dp,\(f_{d,i}\)表示考虑到从高到低第\(d\)位,当前\(t\)个数中有\(i\)个仍然顶上界,并且之前的位都满足异或分别等于\(X\)的限制。......
  • AtCoder Grand Contest 056 D Subset Sum Game
    洛谷传送门AtCoder传送门考虑若\(n\)是奇数怎么做。枚举Alice第一次选的数\(a_i\),然后考虑把剩下的数两两结成一个匹配,若Bob选了其中一个,Alice就选另一个。容易发现排序后奇数位和它右边的偶数位匹配最优。那么设奇数位的和为\(A\),偶数位的和为\(B\),此时Alice获胜......