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\n",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\n",u,p[u].sw);
//printf("merge %d to %d, sward=%d\n",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 染色
写完并查集,挑来挑去发现假了而且改不对。
很难而且没时间,就放弃了,有关的性质也没怎么看出来。
水平不够。