HZOJ 模板(ac)
调了一天,恶心死我了……作者的题解水的一b……
测试点1~6:
暴力修改查询即可,期望得分30。
测试点7~14:
k=1e5,相当于没有限制,那么对于树上每个点建权值线段树,线段树合并即可。
期望的分40,结合算法1 70分。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define MAXN 100010
#define LL long long
#define int LL
#define ma(x,y) memset(x,y,sizeof(x))
using namespace std;
struct edge
{
int u,v,nxt;
#define u(x) ed[x].u
#define v(x) ed[x].v
#define n(x) ed[x].nxt
}ed[MAXN*];
int first[MAXN],num_e;
#define f(x) first[x]
int n,m,k[MAXN],Q,tc[MAXN];
struct tree
{
int ls,rs,sum;
#define ls(x) tr[x].ls
#define rs(x) tr[x].rs
#define sum(x) tr[x].sum
}tr[MAXN*];
int cnt,T[MAXN];
int sum[][],fa[MAXN],maxc;
void add(int &x,int l,int r,int L,int R)
{
if(!x)x=++cnt;
if(l==r){sum(x)|=;return;}
int mid=(l+r)>>;
if(L<=mid)add(ls(x),l,mid,L,R);
if(R>mid) add(rs(x),mid+,r,L,R);
sum(x)=sum(ls(x))+sum(rs(x));
}
int ask(int x,int l,int r,int L,int R)
{
if(l>=L&&r<=R)return sum(x);
int mid=(l+r)>>,ans=;
if(L<=mid)ans+=ask(ls(x),l,mid,L,R);
if(R>mid) ans+=ask(rs(x),mid+,r,L,R);
return ans;
}
int merge(int x,int y)
{
if(!x||!y)return x+y;
ls(x)=merge(ls(x),ls(y));
rs(x)=merge(rs(x),rs(y));
if(!ls(x)&&!rs(x))sum(x)=sum(x)|sum(y);
else sum(x)=sum(ls(x))+sum(rs(x));
return x;
}
int ans[MAXN],x[MAXN],c[MAXN];
void dfs(int x,int fa)
{
for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
{
dfs(v(i),x);
ans[v(i)]=ask(T[v(i)],,maxc,,maxc);
T[x]=merge(T[x],T[v(i)]);
}
}
void dfs2(int x,int ff)
{
fa[x]=ff;
for(int i=f(x);i;i=n(i))
if(v(i)!=ff)
dfs2(v(i),x);
}
inline int read();
inline void adde(int u,int v);
signed main()
{
n=read();int tu,tv;
for(int i=;i<n;i++)
{
tu=read(),tv=read();
adde(tu,tv),adde(tv,tu);
}
for(int i=;i<=n;i++)k[i]=read();
m=read();
dfs2(,);
if(n<=&&m<=)//暴力
{
LL tem,ac;LL x,c;
for(int i=;i<=m;i++)
{
x=read();c=read();
maxc=max(maxc,c);
tem=x;
while(tem)
{
if(sum[tem][]<k[tem])
sum[tem][]++,sum[tem][c]++;
tem=fa[tem];
}
}
Q=read();
for(int i=;i<=Q;i++)
{
tem=read();ac=;
for(int j=;j<=maxc;j++)
if(sum[tem][j]!=)ac++;
printf("%lld\n",ac);
}
return ;
}
for(int i=;i<=m;i++)
{
x[i]=read(),c[i]=read(),tc[i]=c[i];
maxc=max(maxc,c[i]);
}
sort(tc+,tc++m);maxc=m;
int ooo=unique(tc+,tc++m)-tc-;
for(int i=;i<=m;i++)
c[i]=lower_bound(tc+,tc+ooo+,c[i])-tc;
for(int i=;i<=m;i++)
add(T[x[i]],,maxc,c[i],c[i]);
dfs(,);
Q=read();
ans[]=ask(T[],,maxc,,maxc);
for(int i=;i<=Q;i++)
{
x[i]=read();printf("%lld\n",ans[x[i]]);
}
}
inline int read()
{
int s=,f=;char a=getchar();
while(a<''||a>''){if(a=='-')f=-;a=getchar(); }
while(a>=''&&a<=''){s=s*+a-'';a=getchar();}
return s*f;
}
inline void adde(int u,int v)
{
++num_e;
u(num_e)=u;
v(num_e)=v;
n(num_e)=f(u);
f(u)=num_e;
}
测试点15~20:
最恶心的几个点,用到了树上启发式合并+树链剖分思想。
考虑以时间为线段树下标建立权值线段树,维护这段时间内小球的颜色种类数以及小球总数,用一个桶记录每个颜色出现的最早时间。对于每个节点建立一个vector,将关于这个节点的操作color,time二元组扔到这个节点的vector中。
首先一边dfs求出每个点的重儿子,要用重儿子来保证复杂度,证明见这里。
这里先说一下不保证复杂度但是好理解而且可以救回来的思路:
然后第二遍dfs统计答案:先处理儿子(注意每次要清空桶,但是不要memset),然后将这个节点的所有操作扔到他的重儿子的vector中,互换(相当于将重儿子的操作扔到这个节点),然后枚举儿子,将其他儿子的操作合并,接下来就是统计这个点的答案了:首先确保此时桶是空的,然后枚举这个节点vector中的操作,如果当前小球未出现过直接更新线段树,如果在一个更晚的时间出现则在那个时间种类数减1,这个时间种类数+1。最后个数加1.然后说一下线段树的查询操作,和主席树的思想类似(直接上代码):
int ask(int x,int l,int r,int num)
{
if(num<=)return ;
if(l==r)return sum(x);
int ans=,mid=(l+r)>>;
if(num(ls(x))<=num)
{
ans+=sum(ls(x));
ans+=ask(rs(x),mid+,r,num-num(ls(x)));
return ans;
}
else return ask(ls(x),l,mid,num);
}
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define MAXN 100010
#define LL long long
#define MP(a,b) make_pair(a,b)
#define ma(x) memest(x,0,sizeof(x))
using namespace std;
struct edge
{
int u,v,nxt;
#define u(x) ed[x].u
#define v(x) ed[x].v
#define n(x) ed[x].nxt
}ed[MAXN*];
int first[MAXN],num_e;
#define f(x) first[x]
int n,m,k[MAXN],Q,tc[MAXN];
vector<pair<int,int> >cz[MAXN];
struct tree
{
int ls,rs,sum,num;
#define ls(x) tr[x].ls
#define rs(x) tr[x].rs
#define sum(x) tr[x].sum
#define num(x) tr[x].num
}tr[MAXN*];
int cnt,T[MAXN],maxc;
int ans[MAXN],x[MAXN],c[MAXN];
int t[MAXN];
int son[MAXN],size[MAXN];
void add(int &x,int l,int r,int t,int tx,int y)
{
if(!x)x=++cnt;
if(l==r){sum(x)+=tx;num(x)+=y;return;}
int mid=(l+r)>>;
if(t<=mid)add(ls(x),l,mid,t,tx,y);
else add(rs(x),mid+,r,t,tx,y);
sum(x)=sum(ls(x))+sum(rs(x));
num(x)=num(ls(x))+num(rs(x));
}
int ask(int x,int l,int r,int num)
{
if(num<=)return ;
if(l==r)return sum(x);
int ans=,mid=(l+r)>>;
if(num(ls(x))<=num)
{
ans+=sum(ls(x));
ans+=ask(rs(x),mid+,r,num-num(ls(x)));
return ans;
}
else return ask(ls(x),l,mid,num);
}
void merge(int x,int y)
{
for(int i=;i<cz[y].size();i++)
cz[x].push_back(cz[y][i]);
cz[y].clear();
}
void clear(int x)
{
for(int i=;i<cz[x].size();i++)
t[cz[x][i].first]=;
}
void dfs2(int x,int fa)
{
for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
{
dfs2(v(i),x);
clear(v(i));
}
if(son[x])
{
merge(son[x],x);
swap(cz[son[x]],cz[x]);
}
for(int i=f(x);i;i=n(i))
if(v(i)!=fa)merge(x,v(i));
for(int i=;i<cz[x].size();i++)
{
int t1=cz[x][i].first,t2=cz[x][i].second;
if(!t[t1])
add(T[x],,m,t2,,),t[t1]=t2;
else if(t[t1]>t2)
{
add(T[x],,m,t[t1],-,);
add(T[x],,m,t2,,);
t[t1]=t2;
}
add(T[x],,m,t2,,);
}
ans[x]=ask(T[x],,m,k[x]);
}
inline int read();
void dfs(int x,int fa);
inline void adde(int u,int v);
signed main()
{
// freopen("in.txt","r",stdin); n=read();int tu,tv;
for(int i=;i<n;i++)
{
tu=read(),tv=read();
adde(tu,tv),adde(tv,tu);
}
for(int i=;i<=n;i++)k[i]=read();
m=read();
for(int i=;i<=m;i++)
{
x[i]=read(),c[i]=read(),tc[i]=c[i];
maxc=max(maxc,c[i]);
}
sort(tc+,tc++m);maxc=m;
int ooo=unique(tc+,tc++m)-tc-;
for(int i=;i<=m;i++)
c[i]=lower_bound(tc+,tc+ooo+,c[i])-tc;
for(int i=;i<=m;i++)
cz[x[i]].push_back(MP(c[i],i));
dfs(,);
dfs2(,);
Q=read();
for(int i=;i<=Q;i++)
x[i]=read(),printf("%d\n",ans[x[i]]);
}
inline int read()
{
int s=,f=;char a=getchar();
while(a<''||a>''){if(a=='-')f=-;a=getchar(); }
while(a>=''&&a<=''){s=s*+a-'';a=getchar();}
return s*f;
}
inline void adde(int u,int v)
{
++num_e;
u(num_e)=u;
v(num_e)=v;
n(num_e)=f(u);
f(u)=num_e;
}
void dfs(int x,int fa)
{
size[x]=;
for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
{
dfs(v(i),x);
size[x]+=size[v(i)];
if(size[v(i)]>size[son[x]])son[x]=v(i);
}
}
接近正解的T30代码
到这里就结束了,然后你应该就会发现你T30了,因为以上算法是不保证时间复杂度的,原因在于处理重儿子后清空桶花费了过长时间。但是如果不清空桶下面的答案查询就会出错,那怎么办呢?
其实可以在处理完所有轻儿子后在处理重儿子,此后不清空桶,而是直接T[x]=T[son[x]](根节点),直接从重儿子的线段树上开始修改,这样时间复杂度就有保证了。还有一点是这样要在最后才把重儿子的操作合并。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define MAXN 100010
#define LL long long
#define int LL
#define MP(a,b) make_pair(a,b)
#define ma(x) memest(x,0,sizeof(x))
using namespace std;
struct edge
{
int u,v,nxt;
#define u(x) ed[x].u
#define v(x) ed[x].v
#define n(x) ed[x].nxt
}ed[MAXN*];
int first[MAXN],num_e;
#define f(x) first[x]
int n,m,k[MAXN],Q,tc[MAXN];
vector<pair<int,int> >cz[MAXN];
struct tree
{
int ls,rs,sum,num;
#define ls(x) tr[x].ls
#define rs(x) tr[x].rs
#define sum(x) tr[x].sum
#define num(x) tr[x].num
}tr[MAXN*];
int cnt,T[MAXN],maxc;
int ans[MAXN],x[MAXN],c[MAXN];
int t[MAXN];
int son[MAXN],size[MAXN];
void add(int &x,int l,int r,int t,int tx,int y)
{
if(!x)x=++cnt;
if(l==r){sum(x)+=tx;num(x)+=y;return;}
int mid=(l+r)>>;
if(t<=mid)add(ls(x),l,mid,t,tx,y);
else add(rs(x),mid+,r,t,tx,y);
sum(x)=sum(ls(x))+sum(rs(x));
num(x)=num(ls(x))+num(rs(x));
}
int ask(int x,int l,int r,int num)
{
if(num<=)return ;
if(l==r)return sum(x);
int ans=,mid=(l+r)>>;
if(num(ls(x))<=num)
{
ans+=sum(ls(x));
ans+=ask(rs(x),mid+,r,num-num(ls(x)));
return ans;
}
else return ask(ls(x),l,mid,num);
}
void merge(int x,int y)
{
for(int i=;i<cz[y].size();i++)
cz[x].push_back(cz[y][i]);
cz[y].clear();
}
void clear(int x)
{
for(int i=;i<cz[x].size();i++)
t[cz[x][i].first]=;
}
void dfs2(int x,int fa)
{
for(int i=f(x);i;i=n(i))
if(v(i)!=fa&&v(i)!=son[x])
{
dfs2(v(i),x);
clear(v(i));
}
if(son[x])dfs2(son[x],x);
if(son[x])T[x]=T[son[x]];
for(int i=f(x);i;i=n(i))
if(v(i)!=fa&&v(i)!=son[x])merge(x,v(i));
for(int i=;i<cz[x].size();i++)
{
int t1=cz[x][i].first,t2=cz[x][i].second;
if(!t[t1])add(T[x],,m,t2,,),t[t1]=t2;
else if(t[t1]>t2)
{
add(T[x],,m,t[t1],-,);
add(T[x],,m,t2,,);
t[t1]=t2;
}
add(T[x],,m,t2,,);
}
ans[x]=ask(T[x],,m,k[x]);
if(son[x])
{
merge(son[x],x);
swap(cz[son[x]],cz[x]);
}
}
inline int read();
void dfs(int x,int fa);
inline void adde(int u,int v);
signed main()
{
// freopen("in.txt","r",stdin); n=read();int tu,tv;
for(int i=;i<n;i++)
{
tu=read(),tv=read();
adde(tu,tv),adde(tv,tu);
}
for(int i=;i<=n;i++)k[i]=read();
m=read();
for(int i=;i<=m;i++)
{
x[i]=read(),c[i]=read(),tc[i]=c[i];
maxc=max(maxc,c[i]);
}
sort(tc+,tc++m);maxc=m;
int ooo=unique(tc+,tc++m)-tc-;
for(int i=;i<=m;i++)
c[i]=lower_bound(tc+,tc+ooo+,c[i])-tc;
for(int i=;i<=m;i++)
cz[x[i]].push_back(MP(c[i],i));
dfs(,);dfs2(,);
Q=read();
for(int i=;i<=Q;i++)
x[i]=read(),printf("%lld\n",ans[x[i]]);
}
inline int read()
{
int s=,f=;char a=getchar();
while(a<''||a>''){if(a=='-')f=-;a=getchar(); }
while(a>=''&&a<=''){s=s*+a-'';a=getchar();}
return s*f;
}
inline void adde(int u,int v)
{
++num_e;
u(num_e)=u;
v(num_e)=v;
n(num_e)=f(u);
f(u)=num_e;
}
void dfs(int x,int fa)
{
size[x]=cz[x].size();
for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
{
dfs(v(i),x);
size[x]+=size[v(i)];
if(size[v(i)]>size[son[x]])son[x]=v(i);
}
}
完整代码
HZOJ 模板(ac)的更多相关文章
- 7.29 NOIP模拟测试10 辣鸡(ljh)+模板(ac)+大佬(kat)
T1 辣鸡(ljh) 就是一道分类讨论的暴搜,外加一丢丢的减枝,然而我挂了,为啥呢,分类讨论变量名打错,大于小于号打反,能对才怪,写了sort为了调试就注释了,后来忘了解开,小减枝也没打.但是这道题做 ...
- luoguP3808[模板]AC自动机(简单版)
传送门 ac自动机模板题,裸的多串匹配 代码: #include<cstdio> #include<iostream> #include<algorithm> #i ...
- luoguP3796[模板]AC自动机(加强版)
传送门 ac自动机模板,可能我写的ac自动机是有点问题的,所以跑的有些慢 暴力跳fail统计 代码: #include<cstdio> #include<iostream> # ...
- 算法模板——AC自动机
实现功能——输入N,M,提供一个共计N个单词的词典,然后在最后输入的M个字符串中进行多串匹配(关于AC自动机算法,此处不再赘述,详见:Aho-Corasick 多模式匹配算法.AC自动机详解.考虑到有 ...
- 模板 AC自动机
题目描述 有$N$ 个由小写字母组成的模式串以及一个文本串$T$ .每个模式串可能会在文本串中出现多次.你需要找出哪些模式串在文本串$T$ 中出现的次数最多. 输入输出格式 输入格式: 输入含多组数据 ...
- 洛谷.3808/3796.[模板]AC自动机
题目链接:简单版,增强版 简单版: #include <cstdio> #include <cstring> const int N=1e6+5,S=26; char s[N] ...
- [CSP-S模拟测试]:模板(ac)(线段树启发式合并)
题目描述 辣鸡$ljh\ NOI$之后就退役了,然后就滚去学文化课了.他每天都被$katarina$大神虐,仗着自己学过一些姿势就给$katarina$大神出了一道题.有一棵$n$个节点的以$1$号节 ...
- 算法竞赛模板 AC自动机
AC自动机基本操作 (1) 在AC自动机中,我们首先将每一个模式串插入到Trie树中去,建立一棵Trie树,然后构建fail指针. (2) fail指针,是穿插在Trie树中各个结点之间的指针,顾名思 ...
- 模板—AC自动机
#include<iostream> #include<cstdio> #include<cstring> using namespace std; struct ...
随机推荐
- 手残,盘符前边多打一个空格导致的message d:\WEB_APP_QuChongFu\file\五月.xlsx (文件名、目录名或卷标语法不正确。)
尝试读取并解析一个excel文件,一直提示错误 但是有个原始数据,导入就没问题 对比了一下,好像也就是字母d的大小写有区别 我先把大写的D改成小写的试试,如果是大小写问题,那应该抛出异常 好吧,好像并 ...
- solr高亮及摘要
修改了原文的一点内容:原文地址为:http://www.cnblogs.com/rainbowzc/p/3680343.html 高亮显示 两种方法: 1.在程序里通过设置query返回高亮信息 pu ...
- JSP-JSP
JSP(Java Server Page) 1 JSP简介 2 JSP脚本和注释 3 JSP的运行原理 jsp本质上就是Servlet 看在服务器里面提应用就应该明白了 我们可以看下这个源码 目录地址 ...
- Linux监听的网络服务$ netstat -ntlp$ netstat -nulp$ netstat -nxlp
我一般都分开运行这三个命令,不想一下子看到列出一大堆所有的服务.netstat -nalp倒也可以.不过我绝不会用 numeric 选项 (鄙人一点浅薄的看法:IP 地址看起来更方便). 找到所有正在 ...
- CentOS安装fortune+cowsay
1.先找下看有没 2.安装 yum -y install fortune-mod 3.执行fortune 应该可以输出了,接着去弄中文词库,阮一峰的: git clone git@github.com ...
- 微信小程序--轮播图,标题,盒子,tab栏的合成例子
小程序是什么? 微信小程序,是一种不需要下载安装即可使用的应用,用户扫一扫或搜一下即可打开应用,在微信-发现-小程序可打开应用. 一.小程序的样式编写: 目录结构: app.json { " ...
- 转:CentOS上安装LAMP之第三步:MySQL环境及安装过程报错解决方案(纯净系统环境)
这是AMP运行环境中最后配置的环境: 惯例传送门: 1.编译安装MySQL cd /home/zhangatle/tar tar zxvf mysql-.tar.gz cd mysql- cmake ...
- 【Vue】详解组件的基础与高级用法
Vue.js 最核心的功能就是组件(Component),从组件的构建.注册到组件间通信,Vue 2.x 提供了更多方式,让我们更灵活地使用组件来实现不同需求. 一.构建组件 1.1 组件基础 一个组 ...
- Gradle基本操作入手
Gradle本身的领域对象主要由Project和Task.Project为Task提供了执行上下文,所有的Plugin要么向Project中添加用于配置Property,要么向Project中添加不同 ...
- Python re.sub函数