题目

题解(有些小错误)

H老爷的简短题解

请无视题目 $pdf$ 的第二行,信那句话的人都已经上清华了

听说大老爷切了 $250+$ 分,然后发现是两个人分着写三道题的,然后第一题还流假了…… $xswl$


T1

$10\%,\space n\le 16$

枚举

$20\%,\space K\le 10$

发现影响每个位置的决策只有前 $k$ 个,将每相邻 $k$ 个位置压缩成一个二进制状态,做 $DP$。

$20\%,\space Q=0$

分数规划?

强行钦定初始时全部选择正立表演,第 $i$ 个位置若有贡献则要付出 $A_i-B_i$ 的代价(说白了,一个位置如果要把正立表演换成倒立表演就有贡献,否则无贡献),那么每连续 $K$ 个节目中至少有 $P$ 个贡献。

列出 $N-K+1$ 个等式,第 $k$ 个等式为

$$(\sum_{k=i}^{k+K-1}C_k)-Y_i = P \space\space i∈[1,n-K+1],\space Y_i∈[0,K-P-Q]$$

其中 $C_i$ 表示第 $i$ 个位置是否贡献,$Y_i$ 表示 $[k,k+K-1]$ 这个节目段中那些无表演限制的节目中有多少个选择了贡献。

添加第 $0$ 个等式和第 $N-K+2$ 个等式,式子为 $0=0$。然后我们就可以差分了,把每相邻两个式子作差得到:

T2

$50\%,\space n\le 5000$

由于 $50$ 分做法比较显然,我就没考虑 $20$ 分的……给的题解说是枚举两种深度建虚树……

首先看到这种题会有一种直觉:$dis(a,b)=dis(a,c)=dis(b,c)$ 等价于 $dis(a,o)=dis(b,o)=dis(c,o)$,其中 $o$ 是树上一点。

也就是说,我们可以枚举树上一个中点,然后以这个点为根,找三个不同子树中的深度相同的点(即三个点到这个根的距离相等),这三个点就是组成一个合法的无序三元组。

这样会不会漏掉一些三元组?其实不会。

我们从好想的两个点入手证明:

我们要添加一个点 $c$,使得 $dis(a,c)=dis(b,c)$。$dis(a,b)$ 不用考虑,如果 $dis(a,c)=dis(b,c)$ 这个条件能满足的话,让它们两个都等于 $dis(a,c)$ 就行了($dis(a,c)$ 和 $dis(b,c)$ 达不到 $dis(a,b)$ 的情况可以被判掉,先不考虑)。

因为是在树上,所以 $a,b,c$ 三点只能有一条简单路径连接。

如果 $c$ 接在 $a$ 左边,$dis(b,c)$ 就必定大于 $dis(a,c)$,从而无法满足题目要求 $dis(a,b)=dis(a,c)=dis(b,c)$。

$c$ 接在 $b$ 右边同理。

所以 $c$ 只能从 $a,b$ 的简单路径上叉出去。

这时 $dis(a,b)=dis(a,c)=dis(b,c)$ 是有可能的。

设叉出的那个点为 $o$,因为 $dis(a,c)=dis(b,c)$,所以 $dis(a,c)-dis(o,c)=dis(b,c)-dis(o,c)$,$dis(a,o)=dis(b,o)$

同理推出 $dis(a,o)=dis(c,o)$

所以必定存在一个点 $o$ 满足 $dis(a,o)=dis(b,o)=dis(c,o)$, 我们只要枚举这个点 $o$ 即可。

对于枚举的一个点 $o$,设这个点为树根,满足 $dis(a,o)=dis(b,o)=dis(c,o)$ 的点 $a,b,c$ 一定分别在三棵不同子树中(否则在同一子树的两个点的简单路径不经过点 $o$,比如点 $a,c$ 在同一子树中,那这两点的最短路就不是 $dis(a,o)+dis(o,c)$,无法用以上证明来证明当前枚举点是这三点对应的中点 $o$)。

可以发现,同一深度的所有点的组合方案数可以直接列式计算(深度就是一个点到树根 $o$ 的距离)。

由于一开始我理解错题意了,写成了求所有有序三元组的贡献和……不过根据三个数有 $6$ 种排列的性质,可知答案除以 $6$ 就是所有无序三元组的贡献和……所以我直接说有序三元组的贡献和的求法了。

考虑枚举一个深度的一个点(其实就 $dfs$ 搜一下就可以了)。对于这个点,它可以跟其它所有子树的所有同深度的点组成三元组,我们把这个点固定在三元组的第一位,后两位就一定是其它子树中同深度的点了。以后找那些同深度的点时,就会把这个点放在三元组的第二位、第三位,再算一遍贡献相同、顺序不同的三元组。由于每个点都会因此被固定在三个位置各一次,所以可以证明这样一定能算全所有方案。

但是我们显然不可能再用一个或两个循环枚举其它子树中同深度的点,因为现在的复杂度已经是 $O(n^2)$ 了。

我们回头观察一下一个三元组的贡献:$V_a\times V_b + V_a\times V_c + V_b\times V_c$

其实到这里已经可以做了,因为三项是相加,可以拆开算,每项都可以结合预处理做到 $O(1)$ 计算,把三个总和加起来就是固定当前点在第一位时,所有三元组的贡献和了。

题外话:

但我为了少算点东西,取了个巧……

既然同一个三元组会以不同顺序计算 $6$ 次,我们可以换位思考,将一个三元组的贡献改为 $3\times (V_b\times V_c)$,这样这个三元组被以不同顺序计算 $6$ 次后,贡献的总和与原来相同?

这样就只需要算 $V_b\times V_c$,不用预处理其它子树中同深度的所有点的权值和了。

code(取模写得丑见谅哈)

 #include<bits/stdc++.h>
#define rep(i,x,y) for(int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(int i=(x);i>=(y);--i)
#define rep_e(i,u) for(int i=hd[u];i;i=e[i].nxt)
#define ll long long
#define int long long
#define N 5005
#define mod 998244353
#define inv_6 166374059
using namespace std;
inline int read(){
int x=; bool f=; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=;
for(; isdigit(c);c=getchar()) x=(x<<)+(x<<)+(c^'');
if(f) return x;
return -x;
}
int n,V[N];
struct edge{int v,nxt;}e[N<<];
int hd[N],cnt;
inline void add(int u,int v){e[++cnt]=(edge){v,hd[u]}, hd[u]=cnt;}
int c[N][N],c_sum[N],self_pf[N],son_num,cnt_three[N];
bool vis[N],three[N][N];
void dfs(int u,int fa,int dis){
(c[son_num][dis]+=V[u])%=mod, (c_sum[dis]+=V[u])%=mod;
if(!three[son_num][dis]) three[son_num][dis]=, ++cnt_three[dis];
//printf("dfs:%d %d %d\n",u,dis,V[u]);
rep_e(i,u) if(e[i].v!=fa) dfs(e[i].v,u,dis+);
}
int mxDis;
void getPf(int u,int fa,int dis){
if(!vis[dis]) (self_pf[dis]+=c[son_num][dis]*c[son_num][dis]%mod)%=mod, vis[dis]=, mxDis=max(mxDis,dis);
rep_e(i,u) if(e[i].v!=fa) getPf(e[i].v,u,dis+);
}
int ans;
void getAns(int u,int fa,int dis){
if(cnt_three[dis]>=){
int k=(c_sum[dis]*c_sum[dis]%mod-self_pf[dis]-c[son_num][dis]*(c_sum[dis]-c[son_num][dis])*%mod+mod)%mod;
//printf("%d : %d %d %d %d %d\n",u,dis,c_sum[dis]*c_sum[dis],self_pf[dis]*2,c[son_num][dis],c[son_num][dis]*(c_sum[dis]-c[son_num][dis])*2);
//cout<<k<<endl;
(ans+=k*%mod)%=mod;
}
rep_e(i,u) if(e[i].v!=fa) getAns(e[i].v,u,dis+);
}
void clear(int u,int fa,int dis){
c[son_num][dis]=c_sum[dis]=cnt_three[dis]=self_pf[dis]=, three[son_num][dis]=;
rep_e(i,u) if(e[i].v!=fa) clear(e[i].v,u,dis+);
}
signed main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read();
int u,v;
rep(i,,n) u=read(), v=read(), add(u,v), add(v,u);
rep(i,,n) V[i]=read();
rep(i,,n){
//printf("start:%d\n",i);
son_num=;
rep_e(j,i) ++son_num, dfs(e[j].v,i,);
son_num=;
rep_e(j,i) ++son_num, mxDis=, getPf(e[j].v,i,), fill(vis+,vis+mxDis+,);
son_num=;
rep_e(j,i) ++son_num, getAns(e[j].v,i,);
son_num=;
rep_e(j,i) ++son_num, clear(e[j].v,i,);
}
cout<<ans*inv_6%mod<<endl;
return ;
}

$50\%,\space n\le 5000$

群众:蛤,怎么还是 $50$ 分?

别着急,这里用的是推 $dp$ 方程的方法,可以引出正解(我写的 $50$ 分就是小学生做法没什么拓展性)

$100\%$

前置知识:长链剖分

【2019.3.2】NOI 模拟赛的更多相关文章

  1. 『2019/4/9 TGDay2模拟赛 反思与总结』

    2019/4/9 TGDay2模拟赛 今天是\(TG\)模拟赛的第二天了,试题难度也是相应地增加了一些,老师也说过,这就是提高组的难度了.刚开始学难的内容,一道正解也没想出来,不过基本的思路也都是对了 ...

  2. 『2019/4/8 TGDay1模拟赛 反思与总结』

    2019/4/8 TGDay1模拟赛 这次是和高一的学长学姐们一起参加的\(TG\)模拟考,虽然说是\(Day1\),但是难度还是很大的,感觉比\(18\)年的\(Day1\)难多了. 还是看一下试题 ...

  3. NOI模拟赛 Day1

    [考完试不想说话系列] 他们都会做呢QAQ 我毛线也不会呢QAQ 悲伤ING 考试问题: 1.感觉不是很清醒,有点困╯﹏╰ 2.为啥总不按照计划来!!! 3.脑洞在哪里 4.把模拟赛当作真正的比赛,紧 ...

  4. 2019.7.26 NOIP 模拟赛

    这次模拟赛真的,,卡常赛. The solution of T1: std是打表,,考场上sb想自己改进匈牙利然后wei了(好像匈牙利是错的. 大力剪枝搜索.代码不放了. 这是什么神仙D1T1,爆蛋T ...

  5. 6.28 NOI模拟赛 好题 状压dp 随机化

    算是一道比较新颖的题目 尽管好像是两年前的省选模拟赛题目.. 对于20%的分数 可以进行爆搜,对于另外20%的数据 因为k很小所以考虑上状压dp. 观察最后答案是一个连通块 从而可以发现这个连通块必然 ...

  6. 【2019.3.20】NOI模拟赛

    题目 这里必须标记一下那个傻逼问题,再不解决我人就没了! 先放一个 $T3$ $20$ 分暴力 #include<bits/stdc++.h> #define rep(i,x,y) for ...

  7. NOI 模拟赛 #2

    得分非常惨惨,半个小时写的纯暴力 70 分竟然拿了 rank 1... 如果 OYJason 和 wxjor 在可能会被爆踩吧 嘤 T1 欧拉子图 给一个无向图,如果一个边集的导出子图是一个欧拉回路, ...

  8. 【2018.12.10】NOI模拟赛3

    题目 WZJ题解 大概就是全场就我写不过 $FFT$ 系列吧……自闭 T1 奶一口,下次再写不出这种 $NTT$ 裸题题目我就艹了自己 -_-||| 而且这跟我口胡的自创模拟题 $set1$ 的 $T ...

  9. 【2019.8.15 慈溪模拟赛 T1】插头(plugin)(二分+贪心)

    二分 首先,可以发现,最后的答案显然满足可二分性,因此我们可以二分答案. 然后,我们只要贪心,就可以验证了. 贪心 不难发现,肯定会优先选择能提供更多插座的排插,且在确定充电器个数的情况下,肯定选择能 ...

随机推荐

  1. Arguments Optional-freecodecamp算法题目

    Arguments Optional 1.要求 创建一个计算两个参数之和的 function.如果只有一个参数,则返回一个 function,该 function 请求一个参数然后返回求和的结果. 如 ...

  2. Mycat高可用解决方案三(读写分离)

    Mycat高可用解决方案三(读写分离) 一.系统部署规划 名称 IP 主机名称 配置 192.168.199.112 mycat01 2核/2G Mysql主节点 192.168.199.110 my ...

  3. 为啥国内互联网公司都用centos而不是ubuntu?

    一直以来都很好奇ubuntu和centos有啥区别,上学时接触的都是ubuntu,自己每次装virtual box的时候都会下个ubuntu,但是公司的服务器上装的都是centos,今天查了下知乎网友 ...

  4. day03_基本数据类型基本运算

    1.什么是数据类型 变量值才是我们存储的数据,所以数据类指的就是变量值的不同种类 2.为何数据要分类型? 变量值是用来保存现实世界中的状态的,那么针对不同的状态就应该用不同类型的数据去表示 3.如何用 ...

  5. Laravel 命令行常用命令

    一.简介 1.Artisan 是 Laravel 自带的命令行接口名称,它为我们在开发过程中提供了很多有用的命令.想要查看所有可用的Artisan命令,可使用list命令: php artisan l ...

  6. Python中字符串String的基本内置函数与过滤字符模块函数的基本用法

    Python中字符串String的基本内置函数与用法 首先我们要明白在python中当字符编码为:UTF-8时,中文在字符串中的占位为3个字节,其余字符为一个字节 下面就直接介绍几种python中字符 ...

  7. 利用python生成图形验证码

    validCode.py import random from io import BytesIO from PIL import Image, ImageDraw, ImageFont def ge ...

  8. 笔记-python-standard library-17.2 multiprocessing

    笔记-python-standard library-17.2 multiprocessing 1.      multiprocessing source code:Lib/multiprocess ...

  9. Maven使用入门

    Maven使用POM文件管理项目资源,pom.xml文件位于项目根目录下,结构如下: <?xml version="1.0" encoding="UTF-8&quo ...

  10. 可实现一键分享到多个平台(微信,微博,qq空间,人人等)

    友推是一款是面向移动应用的SDK分享组件,提供给开发者集成使用.通过友推,开发者可以轻松集成社会化分享功能,同时创建及管理推荐好友使用您应用的推荐奖励活动,用户推荐好友安装使用您的应用即可获得推荐奖励 ...