2022牛客OI赛前集训营-提高组(第四场) 赛后总结

博弈

https://ac.nowcoder.com/acm/contest/40648/A

时间 名称 赛制 组别 得分 排名
2022.10.11 2022牛客OI赛前集训营(第四场) OI 提高组 200/400 15

A.博弈

之前没用过 hash-xor 所以考试没做出来,采用 set 暴力维护每个节点到根节点的路径上的数然而卡时出问题了 40>040 -> 0

其实不难发现一个结论:如果两节点之间的路径上出现的边权均为偶数次那么先手必败,其余情况先手必胜。

那么很容易想到从反面去做,只要找到必败态的情况即可。

于是先对给定的边权离散化,重新赋值 BASEwBASE^wBASEBASE 这里取 1333113331,ww是离散后的边权)。

然后维护每个节点到根节点的异或和,这样如果两个节点到根节点的异或和相等,那么它们路径上边权出现的次数一定均为偶数。

统计答案用个 unordered_map,时间复杂度 O(nlogn)O(nlogn)

代码:

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

typedef pair<int,unsigned long long> pa;
const int MAXN=5e5+5;
const int BASE=13331;
vector<pa>G[MAXN];
unordered_map<unsigned long long,int>vis;
unsigned long long number[MAXN],sum[MAXN];
int num[MAXN],len;
void dfs(int x,int father)
{
    for(auto i:G[x])
    {
        int y=i.first,z=i.second;
        if(y==father) continue;
        sum[y]=sum[x]^number[lower_bound(num+1,num+len+1,z)-num-1];
        dfs(y,x);
    }
}
int main()
{
    int T;
    cin>>T;
    int n;
    while(T--)
    {
        scanf("%d",&n);
        number[0]=1,vis.clear();
        for(int i=1;i<=n;++i) G[i].clear(),number[i]=number[i-1]*BASE;
        int u,v,w;
        for(int i=1;i<n;++i) scanf("%d %d %d",&u,&v,&w),G[u].push_back({v,w}),G[v].push_back({u,w}),num[i]=w;
        sort(num+1,num+n);
        len=unique(num+1,num+n)-num-1;
        dfs(1,0);
        long long ans=(long long)n*(n-1)/2;
        for(int i=1;i<=n;++i)
        {
            ans-=vis[sum[i]];
            ++vis[sum[i]];
        }
        printf("%lld\n",ans);
    }
    return 0;
}

B.跳跃

思路来源于赛后各AC代码,题解思路过于清奇。

首先维护前缀和 sumisum_i,点 ii 向前跳最多得分 maxlimaxl_i,向后跳最多得分 maxrimaxr_i

然后根据维护的值进行 dp,初始设 dpi,jdp_{i,j} 表示从 11jj 步跳到 ii 的最大得分。

那么可以列出原始转移方程:

dpi,j={k=1n{dpk,j1+sumisumk1}jmod2=0k=n1{dpk,j1+sumksumi1}jmod2=1dp_{i,j}=\begin{cases}\max_{k=1}^{n}\{dp_{k,j-1}+sum_i-sum_{k-1}\}&j \mod 2=0\\\max_{k=n}^{1}\{dp_{k,j-1}+sum_k-sum_{i-1}\}&j \mod 2=1\end{cases}

发现可以压掉一维,以及用前缀/后缀最大值优化,时间复杂度 O(nk)O(nk),然而由于最多 50005000 步内即可更新所有 dp 值,所以时间复杂度 O(5000n)O(5000n)

代码:

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

const int MAXN=1e3+5;
const long long INF=1e18;
int sum[MAXN],maxl[MAXN],maxr[MAXN];
long long dp[MAXN];
int main()
{
	int T;
	cin>>T;
	int n,k;
	while(T--)
	{
		scanf("%d %d",&n,&k);
		for(int i=1;i<=n;++i) scanf("%d",&sum[i]),sum[i]+=sum[i-1];
		int tmp=0;
		for(int i=1;i<=n;++i)
		{
			maxl[i]=sum[i]-tmp;
			tmp=min(tmp,sum[i]);
		}
		tmp=sum[n];
		for(int i=n;i>=1;--i)
		{
			maxr[i]=tmp-sum[i-1];
			tmp=max(tmp,sum[i]);
		}
		memset(dp,-0x3f,sizeof(dp));
		dp[1]=0;
		long long ans=0;
		for(int j=1;j<=min(k,5000);++j)
		{
			if(j&1)
			{
				long long maxn=-INF;
				for(int i=1;i<=n;++i)
				{
					maxn=max(maxn,dp[i]-sum[i-1]);
					dp[i]=maxn+sum[i]>=0?maxn+sum[i]:-INF;
					ans=max(ans,dp[i]+max(0ll,1ll*(k-j)*maxl[i]));
				}
			}
			else
			{
				long long maxn=-INF;
				for(int i=n;i>=1;--i)
				{
					maxn=max(maxn,dp[i]+sum[i]);
					dp[i]=maxn-sum[i-1]>=0?maxn-sum[i-1]:-INF;
					ans=max(ans,dp[i]+max(0ll,1ll*(k-j)*maxr[i]));
				}
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

C.大陆

原题,参见「SCOI2005」王室联邦,连样例都一模一样……

做法是构造,对于每个点,优先满足子树形成一个省,即若当前子树的节点和(包括子树剩余的)大于 BB,省会为当前节点,这个省一定不会大于 B×2B\times2

如果有剩余向上递归,这样可以保证每个省及其省会是连续的。

递归结束后,可能还有一些城市没有找到省会,把这些城市并入根节点所在成市,该省大小不超过 B×3B\times3

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

代码:

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

const int MAXN=1e4+5;
vector<int>G[MAXN];
int stk[MAXN],ans[MAXN],leader[MAXN];
int n,B,top=0,siz=0;
void dfs(int x,int father)
{
	int tmp=top;
	for(auto y:G[x])
	{
		if(y==father) continue;
		dfs(y,x);
		if(top-tmp>=B)
		{
			leader[++siz]=x;
			while(top>tmp) ans[stk[top--]]=siz;
		}
	}
	stk[++top]=x;
}
int main()
{

	cin>>n>>B;
	int u,v;
	for(int i=1;i<n;++i) scanf("%d %d",&u,&v),G[u].push_back(v),G[v].push_back(u);
	dfs(1,0);
	if(!siz) leader[++siz]=1;
	while(top) ans[stk[top--]]=siz;
	cout<<siz<<endl;
	for(int i=1;i<=n;++i) printf("%d ",ans[i]);
	putchar('\n');
	for(int i=1;i<=siz;++i) printf("%d ",leader[i]); 
	return 0;
}

D.排列

蒟蒻不会 fhq-Treap,考场只码了一个40pts的暴力,此处留待以后再补……

//后记&总结:T1完全是没接触这种题型,T2打表发现 dp 数组的变化具有周期性然而并没有想到正解,简单一点的思维题完全没拿到手,T3纯粹是因为做过原题

笔者能力有限,有未述详尽或有误之处,欢迎指正。

全部评论
dalao您的T2G掉了
点赞 回复 分享
发布于 2022-10-20 17:34 四川
巨佬考虑您的 T2 解法是可以卡掉的,例如一组样例 1 998个-1e5 1e5 。对于这组样例正确的输出应该是90029999900001,而按照您的做法输出是 1000000000。
点赞 回复 分享
发布于 2022-10-20 17:33 四川

相关推荐

点赞 评论 收藏
分享
emmm别问我为啥上一条帖子隔了两个月我才开始投简历和拿offer,因为我懒😰简单流程如下:周一凌晨改好的简历,然后到处乱投简历;周二接到了三维家的一面通知,临时抱佛脚的背了一些八股;周三上午一面下午通知第二天hr面;周四上午hr面下午拿offer,遂收手支线:在BOSS上顺手投了几个大厂,投字节的时候不小心投城客户端了,结果过了一天HR突然把我简历要走了,还问我能不能整客户端,我直接一口答应(脏面评警告😢)结果在周三下午的时候给我打电话,说前端有空缺实习岗,问我有没有兴趣,然后就跟我约了周四下午一面😰我都没咋准备啊,咩都不会啊😭结果周四下午面完,晚上打电话通知过一面了,赶紧把二面约在下周一下午,留点缓冲时间。逆大天了,我一半的问题都不会,他居然给我过了?运气未免有点好了😥现在正在恶补计网、网安、性能优化的东西(这三大板块我是几乎一点不会,一面几乎一点答不出来,加上我又没怎么背八股,这块被干烂了😵)心得体会与经验:1.&nbsp;我giao怎么这么快就结束了,我还以为要找好久😨2.&nbsp;大厂的面试问题真的和中厂小厂很大不同,比如在三维家我能自己吹水到vue的数据劫持、Proxy代理响应式之类的他们就觉得很不错了,但是在字节你但凡敢提到一下就会追问你细节了,一追问马脚就全漏出来了3.&nbsp;有信心真的很重要,我感觉我能拿中厂offer最重要的就是吹水吹出自信来了,以至于三维家面试反问面试官有哪里还需要改进的时候,他就说很不错了解的很多😦4.&nbsp;理解很重要,我从头到尾真没背过很多八股,不过有一些知识确实是敲过代码验证过,所以面试的时候能吹水吹得出来😇想了解面经啥的可以直接评论区问我,但我可能也说不全,因为我没有记录,而且今天摆了一天感觉记忆快清空了😵下面是故事时间:我暑假刚开始的时候才开始准备八股,印象很深那个时候连什么原型、事件循环、闭包这些名词都没听过,资料也不知道怎么找,就一直零零散散的准备,感觉也只有js稍微背了一下八股,其他很多时候都是靠完全理解和手写熟悉一些机制的,但这样做效率很低,反正准备了一个多星期半个月就开摆了😭结果一摆就摆到了开学,笔记是乱七八糟的,八股是忘光光的,简历是一直没改的,实习也是一直没投过的。直到上周日晚上偶然和师兄聊天,他突然问我“你怎么还不找实习”,那天晚上才幡然醒悟,是时候做点事情了😡然后就按照上面描述的来走了。其实我感觉我从头到尾都没背特别多八股,也没怎么找刷题资料啥的,早期就是翻尚硅谷或者黑马的入门视频从头学起,中期用面试鸭看了一点点题,主要是在学js机制和敲js代码,后期才发现了w3c的面经网站,然后在那里看着学(那个时候已经懒得敲了,因为有些问题与代码感觉不像是给找实习的看的,忒细了点😂)接下来继续准备字节二面吧,虽然几乎没啥可能可以通过,但是万一有奇迹呢?😍😍😍也祝大家能够早日拿到心仪的offer
我的offer呢😡:我已经预见10天后你会发,节孝子启动了
投递三维家等公司10个岗位
点赞 评论 收藏
分享
评论
4
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务