2021.11.6模拟总结

前言

流程复盘

其实时间规划不够好,主要是第一题写的时间太长了。
写代码+调用了 (50min) 左右,而且是写的 (40pts) 暴力,应该在半个点左右写完比较理想,可能一早上脑子还是不够清醒。
写完 (T1) 基本就过了 (1h+30min) 了,然后去开了个 (T3) 的特判贪心,结果写完了证出来不对,有点小烦,回去写了 (T2) 的暴力记搜。
这些题的暴力都不是特别好写,数据范围正好把最暴力的暴力卡掉。
(T2) 感觉复杂度直逼 (10^8),但是想想暴力应该只能这么写了吧(?
最后剩了 (1h) 打完 (T4) 错误做法并发现假了,赶紧回到 (T3) 写了个假的贪心希望能得分。
期望得分:(40+30+10+0=80pts)
实际得分:(40+10+30+0=80pts)
说实话一开始心里没底,因为不知道大家做的怎么样,毕竟我都没上 (100pts)
结果出来还行,(rk3)

总结

罗列了这么多,需要总结点问题出来。

  • 刚开始做题的时候进入状态较慢,虽然没有好的解决方法,但是还是尽量吧。
  • 接着上一条,打暴力不够稳,这次 (T1) 依赖样例调出了三处错误,尽量要再仔细点。
  • (T3) 又卡在贪心的思路上了,虽然有贪心思想,但实际上是 DP。
  • 不够紧张!!!(不过这有时候也不是没有好处)
  • 水平不够(这根本不用说啊喂!)

题解

T1 game

考场二分写的 (O(n^2 log^2 n)),略微有点悬,实际上不用二分。
直接过程中贪心更新答案即可。
正解带权并查集,看着并不是很复杂,但是实际上感觉很有难度。
首先是根据 (a_i) 的大小关系固定,所以排序。
相当于在维护连通块的时候同时维护每个点的答案。

代码

game-AC
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define FCC fclose(stdin),fclose(stdout)
const int INF = 0x3f3f3f3f,N = 1e6+10;
inline ll read()
{
	ll ret=0ll;char ch=' ',c=getchar();
	while(!(c>='0'&&c<='9')) ch=c,c=getchar();
	while(c>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
	return ch=='-'?-ret:ret;
}
int n,m,maxn;
struct node 
{
	int hp,sw,id;
	bool operator <(const node &oth) const 
	{return hp<oth.hp;}
}p[N];
int head[N<<1],ecnt=-1;
struct edge
{
	int nxt,to;
}a[N<<1];
void init_edge(){memset(head,-1,sizeof(head)),ecnt=-1;}
inline void add_edge(int x,int y)
{
	a[++ecnt]=(edge){head[x],y};
	head[x]=ecnt;
}
int f[N<<1],g[N<<1];
int find(int x)
{
	if(f[x]==x) return x;
	int fa=find(f[x]);
	g[x]=max(g[x],g[f[x]]);
	return f[x]=fa;
}
int main()
{
	init_edge();
	n=read(),m=read();
	for(int i=1;i<=n;i++) p[i].hp=read(),p[i].id=i;
	for(int i=1;i<=n;i++) p[i].sw=read(),p[i].sw=max(p[i].sw,p[i].hp);
	//for(int i=1;i<=n;i++) printf("p[%d]=%d ",p[i].id,p[i].sw);
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		add_edge(u,v),add_edge(v,u);
	}
	sort(p+1,p+n+1); 
	//for(int i=1;i<=n;i++) printf("p[%d]=%d ",p[i].id,p[i].sw);
	for(int i=1;i<=n;i++) 
	{
		f[i+n]=i+n;//建立一个新的虚点,连接两个连通块,防止信息错乱 
		int u=p[i].id;
		for(int j=head[u];~j;j=a[j].nxt)
		{
			int v=a[j].to;
			if(!f[v]) continue;
		//	printf("u=%d,v=%d
",u,v);
			v=find(v);
			if(p[v].sw<p[i].hp) g[v]=p[i].hp;
			p[i+n].sw=max(p[i+n].sw,p[v].sw);
			f[v]=i+n;
		}
		f[u]=i+n;
		g[u]=p[i].hp;		
		p[i+n].sw=max(p[i+n].sw,p[i].sw);
		//printf("sw[%d]=%d
",u,p[u].sw);
		//printf("merge %d to %d, sward=%d
",u,i+n,p[i+n].sw); 
	}
	for(int i=1;i<=n;i++)
	{
		find(i);
		printf("%d ",g[i]); 
	}
	return 0;
}
/*
in1:
4 4
1 3 7 5
2 4 6 8
1 2
2 3 
3 4 
4 1
out1: 5 5 7 5 
in2:
4 3
3 1 2 4
4 2 3 5
1 2
2 3 
3 4 
out2: 3 1 2 4
*/

T2 divide 分治

暴力模拟加记搜只过了 (10pts),想过 (30pts) 需要预处理来 (O(1)) 回答询问,问了 fxj,也需要一点期望的知识,用 (dp(l,r,k)) 记录概率,答案是一个 (Sigma),具体就不展开了。
满分大概是转换模型,对于每一个点单独考虑贡献,当且仅当它是某一段第一个被选,才产生贡献,$O(n) $预处理逆元的前缀和即可做到 (O(1)) 回答某个询问。
实际上这个模型类似于给定一棵有根树,每次随机删去树上某个点以及其子树,求期望多少次能删完。

T3 fief 封地

看来以后假贪心能写还是要写上。
当时证明出来比较 (naive) 的贪心假了之后有点摆烂了,直接换题(换题没问题,但没接着思考就不对了)。
实际根据基本的贪心排序,可以接着设计出 DP,就算不优化也能的高分了。
懒得讲怎么做了...

T4 染色

写完并查集,挑来挑去发现假了而且改不对。
很难而且没时间,就放弃了,有关的性质也没怎么看出来。
水平不够。

原文地址:https://www.cnblogs.com/conprour/p/15518149.html