2017.8.15 总结

今天的题…还好…不是特别难qwq…只不过没想到T2竟然是个结论题,手玩玩出来的(可能是我太傻了吧QAQ),结果只拿了10分…还有这个T1…输入竟然是一个 n 加两个生成数据的seed…刚看到时,我一脸懵逼…

T1

题意:一个人画出了 n 个点,从1n进行标号,并决定使用特殊方法连边。对于第 i 个点(2in),他有两种连边方法:
(1) 将这个点与 1i1 号点都连一条边;
(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 。否则遍历一遍之后,若 num0 ,就是 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(d1),f(d)} 拆成 m 个互不相交的集合,要求这m个集合元素个数和元素之和均相等。
给出任意一种可行解(每行输出一个集合的元素,并在行末输出一个 0 )。(数据保证有解)。

思路:这题,我当时搞了两个小时也没想出什么结果(没想到最后是手玩出结论啊啊啊QAQ)…到最后只拿了一个集合的元素个数与m相等的10分…我是先求出整个 f(c),f(c+1),...,f(d1),f(d) 的序列,然后按如下代码乱搞…( P.S.myset 其实是 vector ,然后 seq 等都是事先求好的)。

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=(dc+1)÷m ,问题转化为将 1(dc+1) 这连续 (dc+1) 项填入一个 n m列的矩阵,每一列对应一个集合。先将这个数列的前 3m 项提出不管,后面的 (m3)n 项通过元素个数为偶数的情况解决(具体不详细讲了qwq)。
通过手玩(大雾)或记结论我们可以得到这样一种做法:
对于第一行,按照正常的顺序 1n 填写。
对于第二行,将 n+12n 按顺序填写:从右边第一个开始,每填一格空一格,填到尽头时,再从右边第一个空格开始,填满所有的空格。
对于第三行,将 2n+13n 按顺序填写:从右边第二个开始,每填一格空一格,填到尽头时,再从右边第一个空格开始,填满所有的空格。
正确性的话..限于篇幅我就不讲了,有兴趣的同学可以自己证一证(我太懒了qwq)。

T3

题意:给定一个以 1 为根的有n个节点的树,一共有 m 次询问。每次询问,给出两个点集A,B
求解 lca(x,y) 深度的最大值,其中 xA,yB
规定 1 号点的深度为1

思路:当时在想这题的时候,我没想出个所以然,于是最后打了个暴力,两重 for 循环遍历点集 A B中的点,每次倍增求个 LCA ,然后与现在记录的最大深度 maxdep 比较,若更大则更新 maxdep 。我在其中加了个剪枝,即:当前扫到的节点 x ,如果dep[x]maxdep,则直接 continue ,因为如果原来节点深度小于 maxdep ,那它的 LCA 的深度绝对不会大于 maxdep 。通过爆搜+剪枝我拿了70…qwq。

题解:假设我们现在处理 x 点,其他任何点与x点的 LCA 只可能是 x x的祖先。而根据 LCA 的不同,这些点可以被分为一些联通块。每一个联通块的内部都是一段连续的 dfs 序。可以证明,有这样一个结论:
依照 dfs 序,若两个点的 dfs 序越接近,则两个点的 LCA 深度越大。
每次询问将其中一个点集按照 dfs 序排序,对于另外一个点集中的每个点,其在这个点集中 dfs 序最近的点。 P.S. 有可能是 dfs 序比它大的,也有可能是比它小的。
时间复杂度为 O(nlogn)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值