今天的题…还好…不是特别难qwq…只不过没想到T2竟然是个结论题,手玩玩出来的(可能是我太傻了吧QAQ),结果只拿了10分…还有这个T1…输入竟然是一个 n 加两个生成数据的
seed …刚看到时,我一脸懵逼…
T1
题意:一个人画出了 n 个点,从
1∼n 进行标号,并决定使用特殊方法连边。对于第 i 个点(2≤i≤n) ,他有两种连边方法:
(1): 将这个点与 1∼i−1 号点都连一条边;
(2): 不作操作(即不向前连边);
他画了很多图,但他不知道哪一些是“美丽的”。一个图是“美丽的”当且仅当这个图存在完美匹配。所以要你帮助他判断哪些图是“美丽的”。
P.S.: 这题的输入文件十分不一样,第一行包含一个正整数 T ,表示数据组数。
对于第t 组数据,第 t+1 行有三个整数 n,seed,seed2
这三个数将用于生成一段数字 p[2...n] , p[i] 表示第 i 个点的连边方案。(并给你了个makedata.cpp/pas )…
画外音:看上去这题一脸…不可做的样子…实际上是送分题(雾)。
思路&&题解:这题第一眼看上去要求完美匹配,是一件非常烦的事情。但是,当再看一遍题目就会发现,其实只用 O(n) 扫一遍就好了qwq。首先, n%2==1 时一定是不行的,因为显然完美匹配的顶点必然有偶数个;其次, p[n] 一定不能是 2 ,若
p[n]==2 ,则第 n 个点无法与前面任何一个点进行匹配。知道这两点后,我们可以从后往前扫一遍,用一个num (初始值为0)的变量来完成这个过程。对于每个点 i ,若p[i]==1 ,则 num++ ;否则, num−− ,若 num<0 ,则直接跳出,输出 No 。否则遍历一遍之后,若 num≥0 ,就是 Yes 。(当时我用了个数组,来模拟栈…是不是很傻233)
以下是 T1 的 makedata 代码:
#include <bits/stdc++.h>
using namespace std;
int p[2000005];
int seed,seed2,n;
int getrand() {
seed=((seed*(12321+n/1000))^9999)%32768;
return seed;
}
int getrand2() {
seed2=((seed2*(12321+n/1000))^9999)%32768;
return seed2;
}
void generateData() {
scanf("%d%d%d",&n,&seed,&seed2);
memset(p,0,sizeof p);
for(int i=2;i<=n;i++)
p[i]=((getrand()/128)%2)^((getrand2()/128)%2);
for(int i=2;i<=n;i++)
p[i]++;
}
int main() {
generateData();
return 0;
}
T2
题意:给你一个从 1 开始(即数列第一项为
f(1) )的等差数列的两项 f(a),f(b) ,给出 c,d,m ,现在要求你将集合 A={f(c),f(c+1),...,f(d−1),f(d)} 拆成 m 个互不相交的集合,要求这m 个集合元素个数和元素之和均相等。
给出任意一种可行解(每行输出一个集合的元素,并在行末输出一个 0 )。(数据保证有解)。
思路:这题,我当时搞了两个小时也没想出什么结果(没想到最后是手玩出结论啊啊啊QAQ)…到最后只拿了一个集合的元素个数与
int now=1;
for(int i=1;i<=len;i++) {
myset[now].push_back(seq[i]);
if(i%x!=0)
now=now%x+1;
}
for(int i=1;i<=m;i++) {
for (unsigned j=0;j<myset[i].size();j++) {
printf("%lld ",myset[i][j]);
}
printf("0\n");
}
return 0;
题解:设 n=(d−c+1)÷m ,问题转化为将 1∼(d−c+1) 这连续 (d−c+1) 项填入一个 n 行
m 列的矩阵,每一列对应一个集合。先将这个数列的前 3⋅m 项提出不管,后面的 (m−3)⋅n 项通过元素个数为偶数的情况解决(具体不详细讲了qwq)。
通过手玩(大雾)或记结论我们可以得到这样一种做法:
对于第一行,按照正常的顺序 1∼n 填写。
对于第二行,将 n+1−2⋅n 按顺序填写:从右边第一个开始,每填一格空一格,填到尽头时,再从右边第一个空格开始,填满所有的空格。
对于第三行,将 2⋅n+1−3⋅n 按顺序填写:从右边第二个开始,每填一格空一格,填到尽头时,再从右边第一个空格开始,填满所有的空格。
正确性的话..限于篇幅我就不讲了,有兴趣的同学可以自己证一证(我太懒了qwq)。
T3
题意:给定一个以 1 为根的有
n 个节点的树,一共有 m 次询问。每次询问,给出两个点集A,B 。
求解 lca(x,y) 深度的最大值,其中 x∈A,y∈B 。
规定 1 号点的深度为1 。
思路:当时在想这题的时候,我没想出个所以然,于是最后打了个暴力,两重
for
循环遍历点集
A
和
题解:假设我们现在处理 x 点,其他任何点与
x 点的 LCA 只可能是 x 或x 的祖先。而根据 LCA 的不同,这些点可以被分为一些联通块。每一个联通块的内部都是一段连续的 dfs 序。可以证明,有这样一个结论:
依照 dfs 序,若两个点的 dfs 序越接近,则两个点的 LCA 深度越大。
每次询问将其中一个点集按照 dfs 序排序,对于另外一个点集中的每个点,其在这个点集中 dfs 序最近的点。 P.S.: 有可能是 dfs 序比它大的,也有可能是比它小的。
时间复杂度为 O(nlogn) 。