CF1326G 题解
题意:
蛛网树是一颗平面树,满足点是该树的凸包的顶点上等价于其是叶子。
给定一个平面树,求有多少种对点集的划分,使得每个划分出来的集合都是蛛网树。
Solution
考虑树形 dp。设 \(f_u\) 是 \(u\) 子树内的划分方案。先考虑两种特殊情况,以 \(u\) 为最浅点的蛛网树只有 \(1,2\) 个点,是容易转移的(因为后面的凸包转移是按边所以这些不会统计到)。
先考虑一个凸包带来的贡献最后应当计算所有凸包带权的和累加到 f 上。设凸包为 \(S\),其带的权应为:
\]
考虑拆一下贡献。

把到叶子的线延长划分出若干区域。把每个区域内的贡献乘起来就可以了。
中间的那些点一定是在树上的路径(判断时有一些细节,不是半平面交起来!)。直接乘起来即可。这样,就解决了计算权值的问题。
还需要知道:哪些点对有贡献?即哪些点对可能成为相邻的叶子?显然,树上路径点必须在连边的一侧(不妨统一设为左侧),然后根据原树上边转移才能保证没有问题。具体来说,不是按照叶子转移,而是先对子树内的边标号,双向边两个方向不同,然后按照相邻叶子在原树上的起始边转移。
这里的转移类似于 P2924 的转移,先把向量排序,再枚举开始的边和按顺序枚举向量转移,叠加答案即可(注意取消掉只有自己的贡献)。
A 是排序后的向量集合。bh 是边的编号,P 向量的 d 是权值,F,S 是开始边和终止边。x,y 是枚举的起点边。这样的起点边当然必须在某个选定象限内。
for(int i=1;i<=tot;i++)gg[i]=0;
gg[bh[x][y]]=1;
for(auto P:A)(gg[P.F]+=gg[P.S]*P.d)%=mod;
(gg[bh[x][y]]+=mod-1)%=mod;
(f[u]+=gg[bh[x][y]])%=mod;
这样的转移显然是 \(O(n^3)\) 的。
为什么不能按点?可以看四号样例的图:

按照原理来讲,不应该有下面这个凸包:

但是按点的统计就不能避免这种情况。根本原因是,点转移不能区分一个点是否被真正当成叶子,像这个 A 点就连了两条边。容易发现按边就不会出任何问题。
最后还有一个问题:这个凸包必须存在一个相邻点对,满足他们在树上的路径包含当前处理 f 数组的 \(u\),实际上就是 \(u\) 在凸包叶子的虚树上。这个很容易保证,dp 时搞一下或者容斥一下均可。
代码在处理权值的时候参考了 std(因为之前一直写的是在半平面交内还能过 21 个点……)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=105,mod=998244353;
struct pt{int x,y;}p[maxn];
pt operator -(pt a,pt b){return {a.x-b.x,a.y-b.y};}
int operator ^(pt a,pt b){return a.x*b.y-a.y*b.x;}
int operator *(pt a,pt b){return a.x*b.x+a.y*b.y;}
int f[maxn],val[maxn][maxn];
#define VI vector<int>
VI lft[maxn][maxn],e[maxn],cur,Ares,E[maxn];
int n,T,dep[maxn];
void dfs(int u,int fa){
cur.push_back(u);
if(u==T)Ares=cur;
for(auto v:E[u]){
if(v==fa)continue;
dfs(v,u);
}
cur.pop_back();
}
VI getpath(int x,int y){
cur.clear();T=y;dfs(x,0);
return Ares;
}
bool check(pt a,pt b,pt c){
return ((b-a)^(c-a))>0;
}
bool good[maxn][maxn];
void dfs2(int u){
cur.push_back(u);
for(auto v:e[u])dfs2(v);
}
VI getsubt(int u){
cur.clear();
dfs2(u);
return cur;
}
int g[maxn];
struct vec{int u,v,d,S,F,ex;};
bool pd(pt a,pt b){
return 1-(b.y>a.y||(b.y==a.y&&b.x>=a.x));
}
bool pd(pt a){
return pd((pt){0,0},a);
}
bool operator <(vec a,vec b){
pt A=p[a.v]-p[a.u],B=p[b.v]-p[b.u];
if(pd(A)!=pd(B))return pd(A)<pd(B);
return (A^B)>0;
}
int Find(int u,const VI &A){
for(auto v:A)if(v==u)return 1;
return 0;
}
bool fat(int x,int y){
VI chain=getpath(x,y);
if(chain.size()==abs(dep[x]-dep[y])+1)return 1;
return 0;
}
bool pd2(pt a,pt b){
if((a^b)!=0)return (a^b)<0;
return (a*b)<0;
}
bool PD(pt a,pt b,pt c){
int x=pd2(a,b),y=pd2(a,c);
if(x!=y)return x<y;
return (b^c)>0;
}
bool dm[maxn][maxn],ckd[maxn][maxn];
vector<int> G[maxn];
int bh[maxn][maxn],gg[maxn*maxn];
void dp(int u){
int prod=1;
for(auto v:e[u]){
dp(v);
(prod*=f[v])%=mod;
}
f[u]=prod;
for(auto v:e[u]){
int p=1;
for(auto w:e[v])(p*=f[w])%=mod;
for(auto v2:e[u])if(v2!=v)(p*=f[v2])%=mod;
(f[u]+=p)%=mod;
}
VI sub=getsubt(u);
for(auto u:sub)G[u].clear();
int tot=0;
for(auto u:sub){
for(auto v:e[u]){
G[u].push_back(v);
G[v].push_back(u);
bh[u][v]=++tot;
bh[v][u]=++tot;
}
}
vector<vec> A;
for(auto x:sub){
for(auto y:sub){
if(x==y||!good[x][y]||dm[x][y])continue;
if(((p[y]-p[x])^(p[u]-p[x]))<0)continue;
VI path=getpath(x,y);int N=path.size()-1,d=1;
for(int i=0;i<=N;i++){
int v=path[i];
pt A,B;
if(i==0)A=p[v]-p[path[i+1]];
else A=p[path[i-1]]-p[v];
if(i==N)B=p[v]-p[path[i-1]];
else B=p[path[i+1]]-p[v];
for(auto w:e[v]){
if(!PD(A,p[w]-p[v],B))continue;
if((i>0&&w==path[i-1])||(i<N&&w==path[i+1]))continue;
(d*=f[w])%=mod;
}
}
bool tp=0;
if(Find(u,path))tp=1;
A.push_back({x,y,d,bh[path[0]][path[1]],bh[path[N]][path[N-1]],tp});
}
}
sort(A.begin(),A.end());
for(auto x:sub){
for(auto y:G[x]){
if(pd(p[x],p[y]))continue;
for(int i=1;i<=tot;i++)gg[i]=0;
gg[bh[x][y]]=1;
for(auto P:A)(gg[P.F]+=gg[P.S]*P.d)%=mod;
(gg[bh[x][y]]+=mod-1)%=mod;
(f[u]+=gg[bh[x][y]])%=mod;
}
}
for(auto x:sub){
for(auto y:G[x]){
if(pd(p[x],p[y]))continue;
for(int i=1;i<=tot;i++)gg[i]=0;
gg[bh[x][y]]=1;
for(auto P:A)if(P.ex==0)(gg[P.F]+=gg[P.S]*P.d)%=mod;
(gg[bh[x][y]]+=mod-1)%=mod;
(f[u]+=mod-gg[bh[x][y]])%=mod;
}
}
}
int fa[maxn];
void dfs1(int u,int f){
fa[u]=f;dep[u]=dep[f]+1;
for(auto v:e[u]){
if(v==f)continue;
dfs1(v,u);
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>p[i].x>>p[i].y;
for(int i=1;i<n;i++){
int u,v;cin>>u>>v;
e[v].push_back(u);
e[u].push_back(v);
E[u].push_back(v);
E[v].push_back(u);
dm[u][v]=dm[v][u]=1;
}
dfs1(1,0);
for(int i=2;i<=n;i++){
VI A;
for(auto u:e[i])if(u!=fa[i])A.push_back(u);
swap(A,e[i]);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j||dm[i][j])continue;
VI chain=getpath(i,j);
bool ck=1;
for(auto u:chain)
if(u!=i&&u!=j&&!check(p[i],p[j],p[u])){
ck=0;break;
}
good[i][j]=ck;
}
}
dp(1);
cout<<f[1]<<endl;
return 0;
}
CF1326G 题解的更多相关文章
- 2016 华南师大ACM校赛 SCNUCPC 非官方题解
我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...
- noip2016十连测题解
以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...
- BZOJ-2561-最小生成树 题解(最小割)
2561: 最小生成树(题解) Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1628 Solved: 786 传送门:http://www.lyd ...
- Codeforces Round #353 (Div. 2) ABCDE 题解 python
Problems # Name A Infinite Sequence standard input/output 1 s, 256 MB x3509 B Restoring P ...
- 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解
题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...
- 2016ACM青岛区域赛题解
A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Jav ...
- poj1399 hoj1037 Direct Visibility 题解 (宽搜)
http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...
- 网络流n题 题解
学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...
- CF100965C题解..
求方程 \[ \begin{array}\\ \sum_{i=1}^n x_i & \equiv & a_1 \pmod{p} \\ \sum_{i=1}^n x_i^2 & ...
- JSOI2016R3 瞎BB题解
题意请看absi大爷的blog http://absi2011.is-programmer.com/posts/200920.html http://absi2011.is-programmer.co ...
随机推荐
- 缓存之ehcache 之使用
1. EHCache 的特点,是一个纯Java ,过程中(也可以理解成插入式)缓存实现,单独安装Ehcache ,需把ehcache-X.X.jar 和相关类库方到classpath中.如项目已安装了 ...
- Tornado框架之模板(三)
知识点 静态文件配置 static_path StaticFileHandler 模板使用 变量与表达式 控制语句 函数 块 目录: 静态文件 static_path 对于静态文件目录的命名,为了便于 ...
- clickhouse之python操作
官网:https://clickhouse-driver.readthedocs.io/en/latest/ 使用python来对clickhouse进行操作 安装) pip install clic ...
- Blazor 组件库 BootstrapBlazor 中Markdown组件介绍
组件介绍 Markdown组件是tui.editor的封装,所以所有内容均基于tui.editor. 默认状态下样子如下所示: 其代码如下: <Markdown Language="@ ...
- Element-UI 中关于 Table 的几个功能点简介(行列的合并和样式、合计行配置等)
〇.前言 本文记录了关于 Element 框架中 Table 的几个功能点,后续将持续更新. el-table 官网地址:https://element.eleme.cn/#/zh-CN/compon ...
- Linux中touch、mkdir与vi的区别
mkdir: 建立一个空目录 touch: 建立一个空文档,但是不进入编辑模式 vi: 建立一个空文档,进入编辑模式
- log4j2 变量注入漏洞(CVE-2021-44228)
log4j2 JNDI注入漏洞(CVE-2021-44228) 概述 本文非常详细的从头到尾debug了CVE-2021-44228漏洞的利用过程,喜欢的师傅记得点个推荐~ Apache Log4j2 ...
- ZCMU-1153
思路 一个感觉是规律问题的数学问题 因为输入的是n所以要的出有关n的关系或者关系 有关排序,所以可以从位次入手,设双胞胎前一个位置在ai,后一个在bi. Sum(bi-ai)=(2+3+4+5+6+. ...
- 奇奇怪怪的编程语言:Malbolge
Malbolge 除了我们日常使用的Python.Java.C等主流编程语言外,还存在这么一类极为晦涩难懂的编程语言,被称为深奥的编程语言(Esoteric programming language, ...
- 鸿蒙NEXT开发案例:九宫格随机
[引言] 在鸿蒙NEXT开发中,九宫格抽奖是一个常见且有趣的应用场景.通过九宫格抽奖,用户可以随机获得不同奖品,增加互动性和趣味性.本文将介绍如何使用鸿蒙开发框架实现九宫格抽奖功能,并通过代码解析展示 ...