打开比赛第一眼——超级树?

点开——原题

百感交集……

欣喜于发现是半年前做过两遍的原题

紧张于如果A不了比较尴尬

绝望于发现根本不会做了

瞟了一眼t1,瞅了一眼t2,嗯……开始搞t3

10分钟打完暴力,开始dp退柿子。然而一个半小时过去了,发现经过一番挫折才终于想起来那个阴间的状态

上了个厕所,看了下表不早了,得开始写前面的了,好恋恋不舍~

再看t2,10分钟打了个很不确定的二分

再看t1,20分钟推了个没有证明的结论

写完都交了

看了下表大概九点半多了,感觉两个小时一事无成,但还是铁下心又回去推t3。

写了好几草稿纸的柿子,手模 \(n=3\) 的情况,老是差那么一点点……

听见隔壁大佬自言自语说t1好难处理,估计是挂了

上厕所看见好多人都在看t2,估计二分是伪了

心里想着算了吧,t3没A就没A吧,估计好多人也忘了

但一想要是现在停下来两个多小时努力就白费了

又一想这题上次也是我讲的肯定是会做哒

再一想只要A掉这道题排名肯定也不会差

于是一直推一直推直到结束也没弄出来……

于是得到了史无前例的糟糕成绩……

题解


好了好了,先回归正题~

A. 星级旅行

首先,要看出这道题的本质是求欧拉路。相当于将每条边复制一下,这时每个点的度数都成为偶数,然后去掉两条边使得整个图有欧拉路

由于这道题里光明正大地说有自环,记自环数量为 \(tot\),于是可以分为三种情况:

  1. 删除两个自环,这时这两个点的度数都减少2(或者一个点减少4),满足题意,答案为 \(C_{tot}^2\)
  2. 删除一个自环和一条边,这时边两端的点度数减少1,成为奇数度点,分别成为起点和终点,答案为 \(tot*(m-tot)\)
  3. 删除两条边,这时有特殊要求是这两条边有一个公共的端点,产生两个奇数度点,此时答案为 \(\sum C_{deg_i}^2\)
查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
const int maxm=4e5+5;
int n,m,deg[maxn],cnt,hd[maxn],tot,num[maxn],num1,x,y;
long long ans;
bool vis[maxm];
int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
return x*f;
}
struct Edge{
int nxt,to;
}edge[maxm];
void add(int u,int v){
edge[++cnt].nxt=hd[u];
edge[cnt].to=v;
hd[u]=cnt;
return ;
}
void dfs(int u,int from){
vis[u]=true;
num[from]++;
for(int i=hd[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(!vis[v])dfs(v,from);
}
return ;
}
int main(){
n=read();
m=read();
// cnt=1;
int root=0;
for(int i=1;i<=m;i++){
x=read();
y=read();
if(x==y){
tot++;
}
else{
add(x,y);
add(y,x);
root=i;
deg[x]++;
deg[y]++;
}
}
for(int i=1;i<=n;i++){
dfs(i,i);
}
for(int i=1;i<=n;i++){
if(num[i]>1)num1++;
}
if(num1>1||tot==m){
cout<<0;
return 0;
}
ans=1ll*tot*(m-tot);
ans+=1ll*tot*(tot-1)/2;
for(int i=1;i<=n;i++){
if(deg[i]>=2)ans+=1ll*deg[i]*(deg[i]-1)/2;
}
cout<<ans;
return 0;
}

B. 砍树

首先说一下错误的做法——二分

这题其实没有单调性(取模运算似乎往往都没有)

比如可以举出栗子

5 10 25

当模数取5的时候答案灰常小,而模数取4和6的时候又会很大

其实嘛……这是一个老老实实推柿子的题

这道题需要满足的条件是

\(\sum_{i=1}^{n} d-(a_i \%d)\le k\),其中1求和部分当 \(a_i\) 模d等于0时以0计算

\(d-\sum_{i=1}^{n} a_i-\left \lfloor \frac{a_i}{d} \right \rfloor * d \le k\)

\(d*n-sum+\sum_{i=1}^{n} \left \lfloor \frac{a_i}{d} \right \rfloor * d \le k\)

\(\sum_{i=1}^{n} \left \lfloor \frac{a_i}{d} \right \rfloor \le \left \lfloor \frac{k+sum}{d} \right \rfloor -n\)

现在再观察柿子,发现右边 \(k+sum\) 和 \(n\) 都是定值,而除 \(d\) 取整特别难受

于是可以想到一个高级科技——整除分块儿~

右边分块后会有根号种取值,每种取值对应 \(d\) 的一段连续区间 \(\left [l,r \right ]\),暴力计算左边验证是否满足条件

观察左式,当 \(d\) 越大时值越小,也越容易满足条件,于是每次只需要贪心地选取区间的 \(r\) 进行验证并更新答案即可

查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=105;
#define int long long
int n,k,a[maxn],sum,tot,ans,l,r,x;
int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
return x*f;
}
bool check(int limit){
int sum1=0;
for(int i=1;i<=n;i++){
int x=a[i]%limit;
if(!x)x=limit;
sum1+=limit-x;
}
if(sum1<=k)return true;
return false;
}
signed main(){
n=read();
k=read();
for(int i=1;i<=n;i++){
a[i]=read();
sum+=a[i];
}
tot=sum+k;
// cout<<check(3);
// cout<<tot<<endl;
while(r<tot){
l=r+1;
x=tot/l;
r=min(tot/x,tot);
// cout<<l<<" "<<r<<" "<<check(r)<<endl;
if(check(r))ans=max(ans,r);
}
cout<<ans;
return 0;
}

C. 超级树

咳咳——这道恶心的题

这回细细地写一下这道题

首先说一下为什么状态不能设计成一维的

像上图一样,在处理当前节点是会有很儿子节点中的两条路径与根节点拼接形成新的路径,而一维状态只能处理当前层节点的信息,于是显然不适用

于是改进,状态为:\(f_{i,j}\) 表示 \(i\) 级超级树中存在 \(j\) 条互不相交的路径的方案数

则答案是 \(f_{k,1}\)

再来看转移,首先观察超级树,长得有一个特点——就是根节点将树劈成了两半,而横跨根节点的路径最多一条。

于是对于 \(i\) 级超级树左子树有 \(j\) 条路径,右子树有 \(k\) 条路径,向 \(i+1\) 级超级树转移时,可以分为四种情况:

  • 不选取根节点,也不拼合路径,此时大的超级树有 \(j+k\) 条路径:\(f_{i+1,j+k}+=f_{i,j}*f_{i,k}\)
  • 选取根节点,不拼合路径,此时根节点自成一条路径,大的超级树有 \(j+k+1\) 条路径:\(f_{i+1,j+k+1}+=f_{i,j}*f_{i,k}\)
  • 选取根节点,根节点与左或右子树中的一条路径拼合,大的超级树有 \(j+k\) 条路径,由于可以接在头也可以接在尾,需要乘2:\(f_{i+1,j+k}+=2*(j+k)*f_{i,j}*f_{i,k}\)
  • 选取根节点,根节点将两条路径串联,大的超级树有 \(j+k-1\) 条路径,由于可以头接尾也可以尾接头,需要乘2:\(f_{i+1,j+k-1} += 2*C_{j+k}^{2}*f_{i,j}* f_ {i,k}\)

回头算一下时间复杂度,发现是 \(n^3\) 的,考虑优化

观察一下转移的柿子和最后的目标,由于每次升级条数最多减少1,而最终条数为1,所以满足 \(i+j+k \le n\) 即可,大大减少复杂度

查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
long long f[305][305],mod;
int main(){
cin>>n>>mod;
f[1][1]=f[1][0]=1;
for(int i=1;i<=n-1;i++){
for(int j=0;j<=n-i+1;j++){
for(int k=0;k<=n-j-i+2;k++){
f[i+1][j+k]=(f[i+1][j+k]+f[i][j]*f[i][k]%mod)%mod;
f[i+1][j+k+1]=(f[i+1][j+k+1]+f[i][j]*f[i][k]%mod)%mod;
f[i+1][j+k]=(f[i+1][j+k]+2ll*f[i][j]%mod*f[i][k]%mod*(j+k)%mod)%mod;
f[i+1][j+k-1]=(f[i+1][j+k-1]+(j+k)*(j+k-1)%mod*f[i][k]%mod*f[i][j]%mod)%mod;
}
}
}
cout<<f[n][1]%mod;
return 0;
}

Summary

平时不考不知道,一考发现竟然做过两遍的题再出来还是不会做,每场考试的改题过程存在很大的漏洞,因此:

  • 看懂思路后独立完成代码
  • 减少改题提交次数
  • A掉题以后花时间沉淀反思
  • 搜集尝试不同的思路
  • 每次考试后跟进博客

而且,万一再有原题——不要惊慌,不要紧张,照常按难度做题即可——不要吊死在一棵树上~

noip模拟测试18的更多相关文章

  1. NOIP模拟测试18(T3待更新)

    T1: 直接模拟,详见代码注释. 复杂度$O(NM)$. Code: #include<iostream> #include<cstdio> #include<vecto ...

  2. 2019.8.12 NOIP模拟测试18 反思总结

    写个博客总是符合要求的对吧 回来以后第一次悄悄参加考试,昨天全程围观… 然后喜提爆炸120分wwwwwwwww T1用了全机房最慢的写法,导致改掉死循环T掉的一个点以后还是死活过不了最后一个点.T2全 ...

  3. NOIP模拟测试18「引子·可爱宝贝精灵·相互再归的鹅妈妈」

    待补 引子 题解 大模拟,注意细节 代码1 #include<bits/stdc++.h> using namespace std; int n,m;char a[1005][1005]; ...

  4. NOIP模拟测试17&18

    NOIP模拟测试17&18 17-T1 给定一个序列,选取其中一个闭区间,使得其中每个元素可以在重新排列后成为一个等比数列的子序列,问区间最长是? 特判比值为1的情况,预处理比值2~1000的 ...

  5. 「题解」NOIP模拟测试题解乱写II(36)

    毕竟考得太频繁了于是不可能每次考试都写题解.(我解释个什么劲啊又没有人看) 甚至有的题目都没有改掉.跑过来写题解一方面是总结,另一方面也是放松了. NOIP模拟测试36 T1字符 这题我完全懵逼了.就 ...

  6. 2019.8.3 [HZOI]NOIP模拟测试12 C. 分组

    2019.8.3 [HZOI]NOIP模拟测试12 C. 分组 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 刚看这题觉得很难,于是数据点分治 k只有1和2两种,分别 ...

  7. 2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色

    2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 数据结构学傻的做法: 对每种颜色开动态开点线段树直接维 ...

  8. 2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci)

    2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci) 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 找规律 找两个节点的lca,需 ...

  9. NOIP模拟测试1(2017081501)

    好,今天是cgg第一次举行模拟测试,希望各位支持. 时间限制:2小时 题目链接: 题目一:水得都没名字了 题目二:车站 题目三:选数 不要觉得2小时太少,我的题目很良心,都很简单. 答案可以在模拟测试 ...

随机推荐

  1. Docker限制

    前言 Docker系列文章: 此篇是Docker系列的第十篇,大家一定要按照我做的Demo都手敲一遍,印象会更加深刻的,马上就开始Kubernetes,加油!一起前行! 为什么要学习Docker Do ...

  2. 【动画消消乐|CSS】调皮逃跑的小方块 077

    前言 Hello!小伙伴! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出-   自我介绍 ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计 ...

  3. SpringBoot数据访问之Druid启动器的使用

    数据访问之Druid启动器的使用 承接上文:SpringBoot数据访问之Druid数据源的自定义使用 官方文档: Druid Spring Boot Starter 首先在在 Spring Boot ...

  4. 自动化可用到的Java读取Excel文件指定的行列数据

    前言 在做接口自动化的时候,通常会遇到数据取用及存放的问题,一般有三种方式可选择 1.数据库存取 2.表格存取 3.项目配置文件存取 这里仅展示下第二种方式表格取数据的 示例 import org.a ...

  5. LaTex公式语法教程及手册(附emlogpro公式显示插件katex说明)

    目录 第一列 第二列 第三列 效果 求和(使用\sum标签) 文本效果 本插件简介 积分(使用\int标签) 文本大小 LaTex是什么 空格 特殊符号 LaTex公式使用教程及手册 定界符 LaTe ...

  6. git submodule 操作

    git submodule foreach git status 举一反三,对所有子库的操作,都可以使用 git submodule foreach 做前缀 foreach,可以记忆为for each ...

  7. linux基础操作命令合集(一)

    linux基础操作命令合集(一) 目录 linux基础操作命令合集(一) 一.命令行提示符 二.切换用户 三.主机名称命令 3.1.临时设置主机名 3.2.永久设置主机名 四.查看系统版本 五.网卡相 ...

  8. 【笔记】scikit-learn中的PCA(真实数据集)

    sklearn中的PCA(真实的数据集) (在notebook中) 加载好需要的内容,手写数字数据集 import numpy as np import matplotlib.pyplot as pl ...

  9. 北航OO第二单元——电梯调度

    三次作业要求简介 特点:目的选层电梯 在电梯的每层入口,都有一个输入装置,让每个乘客输入自己的目的楼层.电梯基于这样的一个目的地选择系统进行调度,将乘客运送到指定的目标楼层. 第一次: 在任意时刻输入 ...

  10. cobaltstrike 框架简述

    关于cobalt strike,火起来也有好几年了,首先感谢大佬们慷慨相助愿意在网上分享和翻译相关资料,让这么好的渗透测试框架工具被更多人知道 那就来整理一下在使用这个框架的过程中我认为需要了解的小知 ...