30pts

令(为1,)为-1;

暴力枚举每个点为起始点的路径,一条路径是合法的当且仅当路径权值和为0且路径上没有出现过负数。

将所有答案算出。

100pts

使用点分治。

要求知道经过重心root的路径,这里默认把root当做树的根。

经过root的路径$ (x,y)$分为两种:

  1. root是路径一端点。
  2. root不是路径\((x,y)\)一端点,可以将路径分为\((x,root.son[x]),root,(root.son[y],y)\),其中\(root.son[x]\)表示\((x,root)\)路径中是root子节点的点。

这样就只需要链的信息。

每个节点记录从\(root.son\)到它的权值和\(val\)。

链\((x,root.son[x] )\)是合法的仅当\(val[x]\)是该路径上最大的。

否则令权值最大的点为\(y\),那么\((x,y)\)的权值和为负数,不合法。

同样的\((root.son[x],x )\)合法仅当\(val[x]\)是该路径上最小的。

同时再记录\((x,root.son[x])\)路径上最大值,\((root.son[x],x)\)上最小值出现的次数,即它可以划分的个数。

具体实现如下:

void add(int f,int x,int Mx,int ct,int Mi,int ct1) {
sum[x]=sum[f]+val[x];
if(sum[x]>Mx)ct=1,Mx=sum[x];
else if(sum[x]==Mx)++ct;
if(sum[x]<Mi)ct1=1,Mi=sum[x];
else if(sum[x]==Mi)++ct1;
if(Mx>=0&&sum[x]==Mx)Max[Mx].push_back(ct);
if(Mi<=0&&sum[x]==Mi)Min[-Mi].push_back(ct1);
++all_si;
travel(x)if(to[q]!=f&&!mark[q])add(x,to[q],Mx,ct,Mi,ct1);
}

合并的时候,要是路径和为0,分三类。

  1. 从根向下
  2. \((x,root.son[x]),(root.son[y],y)\)中某一个值为0
  3. \((x,root.son[x]),(root.son[y],y)\)均不为0

由于root也要算在内,根据root分类。

第一种情况暴力。

if(val[rt]==1) {
rep(w,0,(int)Min[1].size()-1)++ans[Min[1][w]];
} else {
rep(w,0,(int)Max[1].size()-1)++ans[Max[1][w]];
}

第二种情况,先写暴力代码:

if(val[rt]==1) {
if(Max[0].size()&&Min[1].size()) {
rep(w,0,(int)Min[1].size()-1) {
rep(e,0,(int)Max[0].size()-1) {
++ans[Min[1][w]+Max[0][e]];
}
}
}
} else {
if(Max[1].size()&&Min[0].size()) {
rep(w,0,(int)Min[0].size()-1) {
rep(e,0,(int)Max[1].size()-1) {
++ans[Min[0][w]+Max[1][e]];
}
}
}
}

发现其实就是一个卷积的形式。

而且由于vector里面是路径上出现最大或最小的次数,因此必然是值连续的。

故使用FFT复杂度正确,共计\(o(nlog(n^2))\)。

第3种情况两条路拼成一条,因此算S的时候要减1,其他和2一样。

总复杂度$o(nlog(n^2)) $,常数不管。

Code

#include<bits/stdc++.h>
#define rep(q,a,b) for(int q=a,q##_end_=b;q<=q##_end_;++q)
#define dep(q,a,b) for(int q=a,q##_end_=b;q>=q##_end_;--q)
#define mem(a,b) memset(a,b,sizeof a )
#define debug(a) cerr<<#a<<' '<<a<<"___"<<endl
using namespace std;
typedef long long ll;
void in(int &r) {
static char c;
r=0;
while(c=getchar(),!isdigit(c));
do r=(r<<1)+(r<<3)+(c^48);
while(c=getchar(),isdigit(c));
}
const int mn=50005;
ll ans[132000];
const int mod=998244353;
int mlv(int x,int v){
int ans=1;
while(v){
if(v&1)ans=1LL*ans*x%mod;
x=1LL*x*x%mod,v>>=1;
}
return ans;
}
namespace NTT {
const int g=3;
const int gg=332748118;
const int mn=132000;
int to[mn],lim,n,a[mn],b[mn];
void DFT(int* a,int inv) {
rep(q,0,n-1)if(to[q]<q)swap(a[q],a[to[q]]);
for(int len=2; len<=n; len<<=1) {
int sp=len>>1;
ll mv=1;
int ml=mlv(inv?g:gg,(mod-1)/len);
for(int k=0; k<sp; ++k) {
for(int* p=a; p!=a+n; p+=len) {
int mid=1LL*p[k+sp]*mv%mod;
p[k+sp]=(p[k]-mid)%mod;
p[k]=(p[k]+mid)%mod;
}
mv=mv*ml%mod;
}
}
}
void solve(int* a1,int len,int* b1,int len1,int ty) {
n=1,lim=0;
while(n<(len+len1))n<<=1,++lim;
rep(q,0,n-1)to[q]=(to[q>>1]>>1)|(q&1)<<(lim-1);
rep(q,0,len-1)a[q]=a1[q];
rep(q,len,n-1)a[q]=0;
rep(q,0,len1-1)b[q]=b1[q];
rep(q,len1,n-1)b[q]=0;
DFT(a,1),DFT(b,1);
rep(q,0,n-1)a[q]=1LL*a[q]*b[q]%mod;
DFT(a,0);
int inv_n=mlv(n,mod-2);
rep(q,0,n-1)ans[q]+=ty*((1LL*a[q]*inv_n%mod+mod)%mod);
}
}
int head[mn],ne[mn<<1],to[mn<<1],cnt1=1;
#define link(a,b) link_edge(a,b),link_edge(b,a)
#define link_edge(a,b) to[++cnt1]=b,ne[cnt1]=head[a],head[a]=cnt1
#define travel(x) for(int q(head[x]);q;q=ne[q])
int val[mn];
bool mark[mn<<1];
int si[mn],all_si,Mn,root;
void get(int f,int x) {
si[x]=1;
travel(x)if(to[q]!=f&&!mark[q])get(x,to[q]),si[x]+=si[to[q]];
}
void find(int f,int x) {
travel(x)if(to[q]!=f&&!mark[q])find(x,to[q]);
si[x]=max(si[x],all_si-si[x]);
if(si[x]<Mn)Mn=si[x],root=x;
}
int num[mn],sum[mn];
vector<int> Max[mn],Min[mn];
void add(int f,int x,int Mx,int ct,int Mi,int ct1) {
sum[x]=sum[f]+val[x];
if(sum[x]>Mx)ct=1,Mx=sum[x];
else if(sum[x]==Mx)++ct;
if(sum[x]<Mi)ct1=1,Mi=sum[x];
else if(sum[x]==Mi)++ct1;
if(Mx>=0&&sum[x]==Mx)Max[Mx].push_back(ct);
if(Mi<=0&&sum[x]==Mi)Min[-Mi].push_back(ct1);
++all_si;
travel(x)if(to[q]!=f&&!mark[q])add(x,to[q],Mx,ct,Mi,ct1);
}
void clear(int v) {
rep(q,0,v)Max[q].clear(),Min[q].clear();
}
int mid[mn],mid1[mn];
void get_FFT(int a,int b,int ty,int v) {
int mq=0,mw=0;
rep(w,0,(int)Max[a].size()-1)++mid[Max[a][w]],mq=max(mq,Max[a][w]);
rep(w,0,(int)Min[b].size()-1)++mid1[Min[b][w]-v],mw=max(mw,Min[b][w]-v);
NTT::solve(mid,mq+1,mid1,mw+1,ty);
rep(w,0,mq)mid[w]=0;
rep(w,0,mw)mid1[w]=0;
}
void calc(int rt,int v,int ty) {
rep(q,0,v)mid[q]=mid1[q]=0;
if(val[rt]==1) {
rep(q,1,v-1)if(Max[q].size()&&Min[q+1].size())get_FFT(q,q+1,ty,1);
if(Max[0].size()&&Min[1].size())get_FFT(0,1,ty,0);
if(ty==1)rep(w,0,(int)Min[1].size()-1)++ans[Min[1][w]];
} else {
rep(q,1,v-1)if(Max[q+1].size()&&Min[q].size())get_FFT(q+1,q,ty,1);
if(Max[1].size()&&Min[0].size())get_FFT(1,0,ty,0);
if(ty==1)rep(w,0,(int)Max[1].size()-1)++ans[Max[1][w]];
}
} void solve(int x) {
get(0,x);
all_si=si[x],Mn=1e9,root=x;
int mid=all_si;
find(0,x);
int mid_root=root;
travel(mid_root)if(!mark[q]) {
mark[q]=mark[q^1]=1;
solve(to[q]);
mark[q]=mark[q^1]=0;
}
travel(mid_root)if(!mark[q]) {
mark[q]=mark[q^1]=1;
add(0,to[q],-1e9,0,1e9,0);
mark[q]=mark[q^1]=0;
}
calc(mid_root,mid,1);
clear(mid);
travel(mid_root)if(!mark[q]) {
mark[q]=mark[q^1]=1;
all_si=0;
add(0,to[q],-1e9,0,1e9,0);
calc(mid_root,all_si,-1);
clear(all_si);
mark[q]=mark[q^1]=0;
}
}
int main() {
int n,m,a,b;
in(n);
rep(q,1,n-1)in(a),in(b),link(a,b);
char c;
rep(q,1,n) {
while(c=getchar(),c!=')'&&c!='(');
val[q]=c=='('?1:-1;
}
solve(1);
in(m);
rep(q,1,m)in(a),printf("%lld\n",ans[a]);
return 0;
}

「NOI十联测」反函数的更多相关文章

  1. 「NOI十联测」深邃

    「NOI十联测」深邃 要使得最大的连通块最小,显然先二分答案. 先固定1结点为根. 对于一个果实,显然是先处理子树中未分配的点,再向外延伸. 每个结点记录一个\(si[]\),表示子树中未分配的点数, ...

  2. 「NOI十联测」奥义商店

    「NOI十联测」奥义商店 若lzz想花费最少的钱,那么显然要选择数目较少的颜色. 先考虑暴力的写法. 每次向两边统计,每个物品要求被买的概率可以由上一个物品推出. now=1;//now 被买概率 M ...

  3. 「NOI十联测」黑暗

    「NOI十联测」黑暗 \(n\) 个点的无向图,每条边都可能存在,一个图的权值是连通块个数的 \(m\) 次方,求所有可能的图的权值和.(n≤30000,m≤15) 令\(ans[n][m]\)为n个 ...

  4. HHHOJ #153. 「NOI模拟 #2」Kotomi

    抽代的成分远远大于OI的成分 首先把一个点定为原点,然后我们发现如果我们不旋转此时答案就是所有位置的\(\gcd\) 如果要选择怎么办,我们考虑把我们选定的网格边连同方向和大小看做单位向量\(\vec ...

  5. HHHOJ #151. 「NOI模拟 #2」Nagisa

    计算几何板子题(我才没有拷板子的说--) 众所周知,三角形的重心坐标是\((\frac{x_1+x_2+x_3}{3},\frac{y_1+y_2+y_3}{3})\) 然后我们发现如果我们有一个点集 ...

  6. Solution -「NOI 模拟赛」彩色挂饰

    \(\mathcal{Description}\)   给定一个含 \(n\) 个点 \(m\) 条边的简单无向图,设图中最大点双的大小为 \(s\),则保证 \(s\le6\).你将要用 \(k\) ...

  7. Solution -「NOI 模拟赛」出题人

    \(\mathcal{Description}\)   给定 \(\{a_n\}\),求一个 \(\{b_{n-1}\}\),使得 \(\forall x\in\{a_n\},\exists i,j\ ...

  8. 「洛谷 P1801」黑匣子

    好像很久没有更过博客了,因为博主这几周很忙.其实是在搞颓. 题意很难懂,所以就不重复了.其实是懒. 一眼看上去这是个 \(Splay\) 裸题,直接插入一个数,查询区间第 \(K\) 大,但是这样太不 ...

  9. Diary -「NOI 2021」酱油记

    雨幕浓稠 远近一白 是水雾弥漫的天 还是泡沫撑起的海   雨真大呢.   前几天去 ZH 中学集训没啥好记的,就从会合日开始叭. [Day -1]   逃出 ZH,掉入梦麟.(   高中的同学们忘记带 ...

随机推荐

  1. 【系统分析】《系统分析与设计方法》 Jeffrey L.Whitten 第1部分 系统开发环境 第3章 信息系统开发

    1.解释为什么对企业来说,拥有一个标准的系统开发过程很重要. 开发过程的成熟,使项目时间和费用减少,生产率和质量提高 2.如何关联系统生命周期和系统开发方法学? 系统生命周期包含系统开发阶段,其中使用 ...

  2. 文件挂载(一)- Linux挂载Linux文件夹

    一.概述 工作中经常会出现不同服务器.不同操作系统之间文件夹互相挂载的情形,例如文件服务器或数据备份服务器. 挂载一般来说就是以下四种类型: 同类型操作系统 a. linux挂载linux文件夹 b. ...

  3. 比例阀驱动电路后级PWM滤波尖刺如何消除?PWM通过RC低通滤波器模拟DAC

    双头比例阀驱动电路,采用单片机输出2路PWM,分别驱动功率器件(U100的2和4脚),经过U100的8和10脚输出供电电源的高压PWM波形,这个高压PWM经过R104和R114分别采样后经过电流放大器 ...

  4. 编写Java程序_找星座朋友应用软件

    一.About the Project 项目介绍 自古以来,人对于恒星的排列和形状很感兴趣,并很自然地把一些位置相近的星联系起来,组成星座.占星术亦称"占星学"."星占学 ...

  5. .net core使用rabbitmq消息队列 (二)

    之前有写过.net core集成使用rabbitmq的博文,见.net core使用rabbitmq消息队列,但是里面的使用很简单,而且还有几个bug,想改下,但是后来想了想,还是算了,之前使用的是. ...

  6. 表达式树扩展 动态生成表达式树插件 Sy.ExpressionBuilder。

    CURD中,基础查询我感觉还是很烦人的一个浪费时间的工作,我经历过远古时代的GetAll(string name,int age),这种方式写服务的时候真的是心中一万个草泥马飞过,后面逐渐的变成了传一 ...

  7. MongoDB 安装及制作成windows服务

    下载:  注:直接使用浏览器下载速度很慢,建议使用其他下载软件下载(比如:迅雷) 官网下载地址: https://fastdl.mongodb.org/win32/mongodb-win32-x86_ ...

  8. Python中的GIL锁

    在Python中,可以通过多进程.多线程和多协程来实现多任务. 在多线程的实现过程中,为了避免出现资源竞争问题,可以使用互斥锁来使线程同步(按顺序)执行. 但是,其实Python的CPython(C语 ...

  9. PowerShell 管道符之Where-Object的使用方法

    1 Get-Process|Select-Object -Property Name|Where-Object{$_ -match 'QQ'} 可以匹配到QQ为名的结果

  10. JAVA8-STREAM 使用说明

    概述 本人在java开发过程中,有些知识点需要记录整理,我尽量严谨的叙述我学习的经过和心得,以便备份和和大家一起进步学习,此篇文章是在网上多出搜集整理验证,结尾会注明出处,今天学习一个java8新的功 ...