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 ...
随机推荐
- 洛谷 P1036 选数【背包型DFS/选or不选】
题目描述 已知 n 个整数 x1,x2,…,xn,以及一个整数 k(k<n).从 n 个整数中任选 k 个整数相加,可分别得到一系列的和.例如当 n=4,k=3,4 个整数分别为 3,7,12, ...
- python无法启动火狐浏览器且报错“selenium.common.exceptions.WebDriverException: Message: Unable to find a matching set of capabilities”
安装了python2,使用pip安装了selenium,但是在使用时,报了“selenium.common.exceptions.WebDriverException: Message: 'gecko ...
- win10 系统同步时间出错
设置->时间和语言->区域和语言->其他日期,区域和时间设置->设置时间和日期->Internet时间->更改设置 应该会有两个服务器,分别更新下时间,哪个正确就用 ...
- redis书籍
redis中文官网命令网址:http://doc.redisfans.com/ redis英文官网命令网址:https://redis.io/commands redis书籍 由 Karl Segui ...
- python之特点
.python区分大小写:2.注释规范:python使用井号#作为单行注释,且注释的位置,一般放在要注释代码的前一行或这代码的右侧:多行注释则可以用连续三个单引号开始一行,并连续三个单引号在要注释的代 ...
- ML面试1000题系列(51-60)
本文总结ML面试常见的问题集 转载来源:https://blog.csdn.net/v_july_v/article/details/78121924 51.简单说下sigmoid激活函数 常用的非线 ...
- Android 对保存在 sharedpreference的重要数据进行编解码
有时候为了登录方便会将用户名和密码保存在 sharedpreference里面,可是如果不加以处理密码将以明文保存. 在Android中java层提供了工具类:android.util.Base64; ...
- 使用cnpm真的会有诡异的Bug
前端网页居然会出现堆栈溢出,然后网页崩溃,退出的问题. 出现这个Bug的时候,我非常的怀疑我自己的一些操作能力,比如,git的操作. 毕竟我是本地代码然后拉取远程分支,还会暂存自己的代码,然后暂存区代 ...
- 外贸电子商务网站之Prestashop修改顶部导航
如修改以上所示顶部导航. 如何在prestashop顶部导航栏添加链接,Module>Top horizontal menu点击进入Configure页面 1,在Settings 中看到 链接 ...
- vue中element-ui添加按钮
<div v-for="(v,i) in list"> <el-form label-width="120px" size="sma ...