Loading

[省选联考 2023] 题解

D1T1 P9166 火车站

观察题目,联系到以前做过的一些区间 dp 可以发现如果小 A 可以去到(这里是去到而不是最终停在) \(k\) 地点,那么 \(x\)\(k\) 之间的所有地点他都可以去到,因为火车是连续的,不能跳着走,要来到当前地点必须到过路途中的所有节点。

这样子就好办了,分两次处理往左边和往右边走两种情况,这里讨论往右走的情况,往左同理。

我们对于每种情况处理他能去到的最右的地点,具体地,考虑在什么情况下,一个节点会去不了,不难得到处理完所有左端点早于这个节点的轨道后如果还到不了就是真的到不了。

于是我们按照左端点排序,如果遍历到的区间和当前答案区间有交集,就用遍历到的区间右端点更新当前的最右地点,最后判断每个铁轨的右端点在不在当前范围内即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector> 
#define ll long long
using namespace std;
struct Node{
	int l,r;
}a[2000005];
bool cmp(Node x,Node y){
	return x.l<y.l;
}
bool cmp2(Node x,Node y){
	return x.r<y.r;
}
int cnt[2000005];
int main(){
	int n,m,x;
	cin>>n>>m>>x;
	for(int i=1;i<=m;i++)cin>>a[i].l>>a[i].r;
	sort(a+1,a+m+1,cmp);
	int maxlen=x;
	for(int i=1;i<=m;i++){
		if(a[i].r<x || a[i].l>maxlen) continue;
		maxlen=max(maxlen,a[i].r);
	}
	for(int i=1;i<=m;i++) if(a[i].r<=maxlen && a[i].r>=x) cnt[a[i].r]++;
	sort(a+1,a+m+1,cmp2);
	int minlen=x;
	for(int i=m;i>=1;i--){
		if(a[i].l>x || a[i].r<minlen) continue;
		minlen=min(minlen,a[i].l);
	}
	for(int i=1;i<=m;i++) if(a[i].l>=minlen && a[i].l<=x) cnt[a[i].l]++;
	for(int i=1;i<=n;i++)if(i!=x && cnt[i]) cout<<i<<' ';
}

D1T2 P9167 城市建造

简单概括一下题目:询问有多少组边集,满足原图删去后分为若干个联通块,联通块大小的极差不超过 \(k\),如果存在一条边 \((u,v)\),被删除了,称 \(u\)\(v\) 为删除点(并没有删除,只是这么叫),图还得满足每个联通块只有一个删除点。

先考虑题目中的极差,对于 \(k=0\) 的情况,连通块大小肯定是 \(n\) 的因子,而 \(k=1\) 时连通块的大小也只能是 \(\lfloor \frac{n}{s} \rfloor\)\(\lceil \frac{n}{s} \rceil\) 的(\(s\) 为 1 到 \(n\) 之间任意一个整数),结合整除分块的相关知识可以知道所有可能的联通块大小组合只有不超过 \(2\sqrt n\) 种,这复杂度加个线性还在我们的接受范围之内,继续考虑其他条件。

再先考虑有关删除点的条件,这个条件只与点有关,而且考虑边的话解的判断和求解不太好实现,于是试着把删除边转化为选择点,这里的选择指的是找到一些点放到一个联通块里。

类似 dp 中构造转移方程,考虑在已有点的情况下还需要和还可以加入哪些点,首先,我们发现如果存在两个点在已选择的点集内,且都有向外连的边,设其为 \((a,b)\)\((c,d)\),那么 \(c\)\(d\) 我们一定要选择一个点,否则就会导致删除点不只一个。

\(c\)\(d\) 是同一个点怎么办呢?这等价于存在一个点有两条不同的到达选择的点集的路径(只用终点不同就行),可以看出这个点也必须选择。前面的表述是否有些熟悉?这与我们对点双连通分量的定义有些相似,把它套进去可以发现对于一个点双中的点,只要有两个被选到同一个联通块,其他点也必须选到那个联通块中。

点双和选择问题,自然的想到建出圆方树进行 dp 处理,我们设 \(siz_u\) 表示子树 \(u\) 的圆点个数,\(f_u\) 表示子树 \(u\) 能单独形成合法解(能被分成若干个满足上述条件的联通块)的合法解的数量,设计转移方程:

如果 \(u\) 是圆点:

  • \(siz_v < siz_u\),此时肯定不能把 \(v\) 单独割下来,使 \(tot+=siz_v\)

  • \(siz_v = siz_u\),此时 \(v\) 可以割也可以不割,考虑 \(f_v\),如果 \(f_v=0\),使 \(tot+=siz_v\),否则使 \(cnt\) 变为 \(cnt+1\)

  • \(siz_v > siz_u\),此时必须割 \(v\),使 \(ned\) 变为 \(ned \cdot f_v\)

\(tot\) 初值为 1,可以看到我们记录了很多变量,现在考虑统计答案,首先,如果 \(d \leq tot \leq d+1\)\(d\) 为枚举出的联通块大小),那么 \(f_i\) 加上 \(ned\),若 $ tot > d $,肯定无解,剩下情况只有 \(tot=1\) 时有可能有解,此时答案加上 \(ned \cdot cnt\) 表示考虑 \(cnt\) 种多余的割还是不割,最多只能不割一个,我们考虑不割哪个后剩下就只有 \(ned\) 种情况,二者相乘就得到了结果,注意第二种点既可以合并也可以不合并,第一和第三种情况可以共存,而 \(k=0\) 时同理。

对于方点,$f_u = \Pi_{v \in son(u)} f_v $,这个转移方程的意义在于只考虑把每个圆点各自分开的情况,不过可以发现全部合起来的情况会在其父亲圆点那里被考虑,因此不用进行处理。

最后,我们还差一个根节点没有确定,由于我们对于状态的特殊定义,根节点若是圆点,必须是对应联通块的删除节点,若是方点,子节点必须全部分割,可以发现重心刚好符合我们的要求,若其不是删除节点,那么该联通块要么有不止一个删除节点,要么对应联通块大小肯定大于 \(\frac{n}{2}\),若是方点则同理。

由于常数较大,我们还需要加入一点剪枝,例如,当 \(siz_u<d\) 时不进行处理,当 \(siz_u>d\)\(f_u=0\) 时直接无解等,于是此题得到解决。

D1T3 P9168 人员调度

看题目,如果没有修改的话不难想到一个贪心:把员工按照能力值排序,如果子树内还有空间就放进去,而为了避免安排员工的操作,我们把这里的“放”当做把当前节点储存的员工加一,查询的时候直接查询子树内的总员工。

可以证明这样的操作是正确的,只要是由这种操作构造的解法一定有一种安排方法,而一种合法的安排方法也一定可以以这种形式表示,而排序操作保证了最优性,因为没放进来的肯定整个子树所有数的能力值都比他高。

那带修改了怎么办呢,多次修改再采取这个操作的时间复杂度是 \(O(nq)\) 的,我们得考虑抛离了排序怎么做,考虑加入一个员工会对整颗树造成什么影响,类似上一步,只要子树内有空间无脑放,没空间就找子树内最小的扔出来再把它放进去。

又发现这个操作我们刚好可以通过树链剖分加上线段树实现,那剩下的就只剩删除操作了,发现每个员工有固定的出现时间段,直接套路转成线段树分治,时间复杂度 \(O(n\log^3 n)\)

代码待补

D2T1 P9169 过河卒

观察数据范围并结合题目,由于地图不变,可以知道本题中最重要的就是棋子的状态,而状态数又刚好在我们的接受范围之内,于是考虑枚举每一个状态,向能到达的点连边,注意这里每个状态有两种情况,分别是双方先手,需要注意的是这两种情况是不同的,需要拆点。

接着,由于题目要求的是是否必胜以及步数,而我们能确定一些状态必败,结合博弈论,考虑对于当前节点,如果是 A 先手,而且出边中有对于 B 的必败状态,那么可知当前状态 A 一定必胜,最小步数就是这些状态中最小步数 \(+1\);否则 A 必败。由此,我们找到每个必败节点,在反图上跑拓扑排序即可,由于没人想输,所以这种方式跑不到的状态就是平局。

代码待补

posted @ 2023-04-01 19:59  eastcloud  阅读(262)  评论(0编辑  收藏  举报