「HNOI2016」数据结构大毒瘤
真是 \(6\) 道数据结构毒瘤。。。
开始口胡各种做法。。。
「HNOI2016」网络
整体二分+树状数组。
开始想了一个大常数 \(O(n\log^2 n)\) 做法,然后就被卡掉了。。。
发现直接维护一定是 \(O(n\log^3 n)\) 的,所以我当时选择了用 \(LCT\) 维护树上路径,跑起来比树剖可能都慢。。。
其实路径加单点查可以直接在 \(dfs\) 序上弄树状数组的,虽然也是 \(O(n\log^2 n)\) 的,但是肯定能通过此题。。。
\(Code\ Below:\)
#include <bits/stdc++.h>
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const int maxn=100000+10;
const int maxm=200000+10;
int n,m,qn,c[maxn],ans[maxm],head[maxn],to[maxn<<1],nxt[maxn<<1],tot;
int top[maxn],dep[maxn],siz[maxn],son[maxn],fa[maxn],id[maxn],tim;
struct Query
{
int op,x,y,z,lca,id;
}p[maxm],q[maxm],q1[maxm],q2[maxm];
inline int read()
{
register int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return (f==1)?x:-x;
}
void print(int x)
{
if(x<0){putchar('-');x=-x;}
if(x>9) print(x/10);
putchar(x%10+'0');
}
inline void add(int x,int y)
{
for(;x<=n;x+=lowbit(x)) c[x]+=y;
}
inline int sum(int x)
{
int ans=0;
for(;x;x-=lowbit(x)) ans+=c[x];
return ans;
}
inline void update(Query q,int z)
{
add(id[q.x],z);add(id[q.y],z);add(id[q.lca],-z);
if(fa[q.lca]) add(id[fa[q.lca]],-z);
}
inline int query(int x)
{
return sum(id[x]+siz[x]-1)-sum(id[x]-1);
}
inline void addedge(int x,int y)
{
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs1(int x,int f)
{
siz[x]=1;fa[x]=f;dep[x]=dep[f]+1;
int maxson=-1;
for(int i=head[x],y;i;i=nxt[i])
{
y=to[i];
if(y==f) continue;
dfs1(y,x);siz[x]+=siz[y];
if(siz[y]>maxson) maxson=siz[y],son[x]=y;
}
}
void dfs2(int x,int topf)
{
top[x]=topf;id[x]=++tim;
if(son[x]) dfs2(son[x],topf);
for(int i=head[x],y;i;i=nxt[i])
{
y=to[i];
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
inline int LCA(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
return x;
}
void solve(int L,int R,int l,int r)
{
if(L>R) return ;
if(l==r)
{
int cnt=0;
for(int i=L;i<=R;i++)
{
if(q[i].op==0) cnt++,update(q[i],1);
if(q[i].op==1) cnt--,update(p[q[i].id],-1);
if(q[i].op==2)
{
if(query(q[i].x)==cnt) ans[q[i].id]=-1;
else ans[q[i].id]=l;
}
}
for(int i=L;i<=R;i++)
{
if(q[i].op==0) update(q[i],-1);
if(q[i].op==1) update(p[q[i].id],1);
}
return ;
}
int mid=(l+r)>>1,cnt=0,cnt1=0,cnt2=0;
for(int i=L;i<=R;i++)
{
if(q[i].op==0)
{
if(q[i].z<=mid) q1[++cnt1]=q[i];
else cnt++,update(q[i],1),q2[++cnt2]=q[i];
}
if(q[i].op==1)
{
if(p[q[i].id].z<=mid) q1[++cnt1]=q[i];
else cnt--,update(p[q[i].id],-1),q2[++cnt2]=q[i];
}
if(q[i].op==2)
{
if(query(q[i].x)==cnt) q1[++cnt1]=q[i];
else q2[++cnt2]=q[i];
}
}
for(int i=L;i<=R;i++)
{
if(q[i].op==0&&q[i].z>mid) update(q[i],-1);
if(q[i].op==1&&p[q[i].id].z>mid) update(p[q[i].id],1);
}
for(int i=1;i<=cnt1;i++) q[L+i-1]=q1[i];
for(int i=1;i<=cnt2;i++) q[L+cnt1+i-1]=q2[i];
solve(L,L+cnt1-1,l,mid);solve(L+cnt1,L+cnt1+cnt2-1,mid+1,r);
}
int main()
{
n=read(),m=read();
int x,y;
for(int i=1;i<n;i++)
{
x=read(),y=read();
addedge(x,y),addedge(y,x);
}
dfs1(1,0);dfs2(1,1);
for(int i=1;i<=m;i++)
{
q[i].op=read();
if(q[i].op==0) q[i].x=read(),q[i].y=read(),q[i].z=read(),q[i].lca=LCA(q[i].x,q[i].y);
if(q[i].op==1) q[i].id=read();
if(q[i].op==2) q[i].x=read(),q[i].id=++qn;
p[i]=q[i];
}
solve(1,m,1,1e9);
for(int i=1;i<=qn;i++) print(ans[i]),putchar('\n');
return 0;
}
附上一个 \(LCT\) 版被卡常的版本:
#include <bits/stdc++.h>
using namespace std;
const int maxn=100000+10;
const int maxm=200000+10;
int n,m,qn,id[maxm],ans[maxm],head[maxn],to[maxn<<1],nxt[maxn<<1],tot;
struct Query
{
int op,x,y,z,id;
}p[maxm],q[maxm],q1[maxm],q2[maxm];
inline int read()
{
register int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return (f==1)?x:-x;
}
void print(int x)
{
if(x<0){putchar('-');x=-x;}
if(x>9) print(x/10);
putchar(x%10+'0');
}
namespace LCT
{
int ch[maxn][2],fa[maxn],val[maxn],tag[maxn],sta[maxn],top;bool rev[maxn];
inline void reverse(int x)
{
rev[x]^=1;
swap(ch[x][0],ch[x][1]);
}
inline void pushtag(int x,int y){val[x]+=y;tag[x]+=y;}
inline void pushdown(int x)
{
if(tag[x])
{
if(ch[x][0]) pushtag(ch[x][0],tag[x]);
if(ch[x][1]) pushtag(ch[x][1],tag[x]);
tag[x]=0;
}
if(rev[x])
{
if(ch[x][0]) reverse(ch[x][0]);
if(ch[x][1]) reverse(ch[x][1]);
rev[x]=0;
}
}
inline bool nrt(int x){return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}
inline void rotate(int x)
{
int y=fa[x],z=fa[y],k=(ch[y][1]==x),u=ch[x][k^1];
if(nrt(y)) ch[z][ch[z][1]==y]=x;
ch[y][k]=u;ch[x][k^1]=y;
if(u) fa[u]=y;fa[y]=x;fa[x]=z;
}
inline void splay(int x)
{
int y,z;top=0;
for(y=x;nrt(y);y=fa[y]) sta[++top]=y;
pushdown(y);
while(top) pushdown(sta[top--]);
while(nrt(x))
{
y=fa[x],z=fa[y];
if(nrt(y)) rotate((ch[y][1]==x)^(ch[z][1]==y)?x:y);
rotate(x);
}
}
inline void access(int x)
{
for(int y=0;x;y=x,x=fa[x])
splay(x),ch[x][1]=y;
}
inline void makeroot(int x)
{
access(x),splay(x),reverse(x);
}
inline int findroot(int x)
{
access(x),splay(x);
for(;ch[x][0];x=ch[x][0]) pushdown(x);
return x;
}
inline void split(int x,int y)
{
makeroot(x),access(y),splay(y);
}
inline void link(int x,int y){fa[x]=y;}
}
inline void addedge(int x,int y)
{
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs(int x,int f)
{
if(f) LCT::link(x,f);
for(int i=head[x],y;i;i=nxt[i])
if((y=to[i])!=f) dfs(y,x);
}
inline void calc(int x,int y,int z)
{
LCT::split(x,y);LCT::pushtag(y,z);
}
void solve(int L,int R,int l,int r)
{
if(L>R) return ;
// printf("solve(%d, %d, %d, %d)\n",L,R,l,r);
// for(int i=L;i<=R;i++)
// {
// if(q[i].op==0) printf("%d %d %d %d %d\n",q[i].op,q[i].x,q[i].y,q[i].z);
// if(q[i].op==1) printf("%d %d\n",q[i].op,q[i].id);
// if(q[i].op==2) printf("%d %d\n",q[i].op,q[i].x);
// }
// printf("\n");
if(l==r)
{
int cnt=0;
for(int i=L;i<=R;i++)
{
if(q[i].op==0) cnt++,calc(q[i].x,q[i].y,1);
if(q[i].op==1) cnt--,calc(p[q[i].id].x,p[q[i].id].y,-1);
if(q[i].op==2)
{
LCT::makeroot(q[i].x);
if(LCT::val[q[i].x]==cnt) ans[q[i].id]=-1;
else ans[q[i].id]=l;
}
}
for(int i=L;i<=R;i++)
{
if(q[i].op==0) calc(q[i].x,q[i].y,-1);
if(q[i].op==1) calc(p[q[i].id].x,p[q[i].id].y,1);
}
return ;
}
int mid=(l+r)>>1,cnt=0,cnt1=0,cnt2=0;
for(int i=L;i<=R;i++)
{
if(q[i].op==0)
{
if(q[i].z<=mid) q1[++cnt1]=q[i];
else cnt++,calc(q[i].x,q[i].y,1),q2[++cnt2]=q[i];
}
if(q[i].op==1)
{
if(p[q[i].id].z<=mid) q1[++cnt1]=q[i];
else cnt--,calc(p[q[i].id].x,p[q[i].id].y,-1),q2[++cnt2]=q[i];
}
if(q[i].op==2)
{
LCT::makeroot(q[i].x);
if(LCT::val[q[i].x]==cnt) q1[++cnt1]=q[i];
else q2[++cnt2]=q[i];
}
}
for(int i=L;i<=R;i++)
{
if(q[i].op==0&&q[i].z>mid) calc(q[i].x,q[i].y,-1);
if(q[i].op==1&&p[q[i].id].z>mid) calc(p[q[i].id].x,p[q[i].id].y,1);
}
for(int i=1;i<=cnt1;i++) q[L+i-1]=q1[i];
for(int i=1;i<=cnt2;i++) q[L+cnt1+i-1]=q2[i];
solve(L,L+cnt1-1,l,mid);solve(L+cnt1,L+cnt1+cnt2-1,mid+1,r);
}
int main()
{
n=read(),m=read();
int x,y;
for(int i=1;i<n;i++)
{
x=read(),y=read();
addedge(x,y),addedge(y,x);
}
dfs(1,0);
for(int i=1;i<=m;i++)
{
q[i].op=read();
if(q[i].op==0) q[i].x=read(),q[i].y=read(),q[i].z=read();
if(q[i].op==1) q[i].id=read();
if(q[i].op==2) q[i].x=read(),q[i].id=++qn;
p[i]=q[i];
}
solve(1,m,1,1e9);
for(int i=1;i<=qn;i++) print(ans[i]),putchar('\n');
return 0;
}
「HNOI2016」序列
只会莫队,不适轻喷。。。
我们对于端点的移动计算贡献。
比如从 \([l,r]\) 到 \([l,r+1]\) 时我们考虑左端点在 \([l,r[\) 右端点在 \(r+1\) 的答案。
首先找到 \([l,r]\) 最小值的位置 \(k\),左端点在 \([l,k]\) 的贡献为 \((k-l+1)\times a_k\)
那么现在维护一个 \(lsum,rsum\),表示左端点/右端点固定时的答案,此时左端点在 \([k+1,r]\) 的贡献为 \(rsum[r+1]-rsum[k]\)
左端点的移动也是类似的,时间复杂度 \(O(n\sqrt{n})\)
\(Code\ Below:\)
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=100000+10;
int n,m,a[maxn],lg[maxn],Min[maxn][18],sta[maxn],top,blo,L,R;
ll lsum[maxn],rsum[maxn],ans[maxn],now;
struct Query{
int l,r,id;
}q[maxn];
inline bool cmp(const Query &a,const Query &b){
if((a.l-1)/blo!=(b.l-1)/blo)
return (a.l-1)/blo<(b.l-1)/blo;
return a.r<b.r;
}
inline int read(){
register int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return (f==1)?x:-x;
}
inline int query(int l,int r){
int k=lg[r-l+1];
return a[Min[l][k]]<a[Min[r-(1<<k)+1][k]]?Min[l][k]:Min[r-(1<<k)+1][k];
}
inline void addl(int x){
int k=query(L,R);
now+=lsum[L]-lsum[k]+1ll*(R-k+1)*a[k];
}
inline void dell(int x){
int k=query(L,R);
now-=lsum[L]-lsum[k]+1ll*(R-k+1)*a[k];
}
inline void addr(int x){
int k=query(L,R);
now+=rsum[R]-rsum[k]+1ll*(k-L+1)*a[k];
}
inline void delr(int x){
int k=query(L,R);
now-=rsum[R]-rsum[k]+1ll*(k-L+1)*a[k];
}
int main()
{
n=read(),m=read();blo=sqrt(n)+1;
for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;i++) a[i]=read(),Min[i][0]=i;
for(int j=1;j<18;j++)
for(int i=1;i+(1<<j)-1<=n;i++) Min[i][j]=a[Min[i][j-1]]<a[Min[i+(1<<(j-1))][j-1]]?Min[i][j-1]:Min[i+(1<<(j-1))][j-1];
for(int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].id=i;
for(int i=1;i<=n;i++){
while(top&&a[sta[top]]>a[i]) top--;
rsum[i]=rsum[sta[top]]+1ll*(i-sta[top])*a[i];sta[++top]=i;
}
for(int i=n;i>=1;i--){
while(top&&a[sta[top]]>=a[i]) top--;
lsum[i]=lsum[sta[top]]+1ll*(sta[top]-i)*a[i];sta[++top]=i;
}
sort(q+1,q+m+1,cmp);
L=1,R=0;
for(int i=1;i<=m;i++){
while(R<q[i].r) R++,addr(R);
while(R>q[i].r) delr(R),R--;
while(L<q[i].l) dell(L),L++;
while(L>q[i].l) L--,addl(L);
ans[q[i].id]=now;
}
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}
「HNOI2016」数据结构大毒瘤的更多相关文章
- 「HNOI2016」树 解题报告
「HNOI2016」树 事毒瘤题... 我一开始以为每次把大树的子树再接给大树,然后死活不知道咋做,心想怕不是个神仙题哦 然后看题解后才发现是把模板树的子树给大树,虽然思维上难度没啥了,但是还是很难写 ...
- 「HNOI2016」序列 解题报告
「HNOI2016」序列 有一些高妙的做法,懒得看 考虑莫队,考虑莫队咋移动区间 然后你在区间内部找一个最小值的位置,假设现在从右边加 最小值左边区间显然可以\(O(1)\),最小值右边的区间是断掉的 ...
- 「HNOI2016」网络 解题报告
「HNOI2016」网络 我有一个绝妙的可持久化树套树思路,可惜的是,它的空间是\(n\log^2 n\)的... 注意到对一个询问,我们可以二分答案 然后统计经过这个点大于当前答案的路径条数,如果这 ...
- 「HNOI2016」最小公倍数 解题报告
「HNOI2016」最小公倍数 考虑暴力,对每个询问,处理出\(\le a,\le b\)的与询问点在一起的联通块,然后判断是否是一个联通块,且联通块\(a,b\)最大值是否满足要求. 然后很显然需要 ...
- loj #2051. 「HNOI2016」序列
#2051. 「HNOI2016」序列 题目描述 给定长度为 n nn 的序列:a1,a2,⋯,an a_1, a_2, \cdots , a_na1,a2,⋯,an,记为 a[1: ...
- LOJ #2048. 「HNOI2016」最小公倍数
题意 有 \(n\) 个点,\(m\) 条边,每条边连接 \(u \Leftrightarrow v\) 且权值为 \((a, b)\) . 共有 \(q\) 次询问,每次询问给出 \(u, v, q ...
- 【LOJ】#2052. 「HNOI2016」矿区
题解 之前尝试HNOI2016的时候弃坑的一道,然后给补回来 (为啥我一些计算几何就写得好长,不过我写啥都长orz) 我们尝试给这个平面图分域,好把这个平面图转成对偶图 怎么分呢,我今天也是第一次会 ...
- 「HNOI2016」大数
题目描述 给定一个质数\(p\)和一个数字序列,每次询问一段区间\([l,r]\), 求出该序列区间\([l,r]\)内的所有子串,满足该子串所形成的数是\(p\)的倍数(样例的解释也挺直观的) 基本 ...
- loj#2049. 「HNOI2016」网络(set 树剖 暴力)
题意 题目链接 Sol 下面的代码是\(O(nlog^3n)\)的暴力. 因为从一个点向上只会跳\(logn\)次,所以可以暴力的把未经过的处理出来然后每个点开个multiset维护最大值 #incl ...
随机推荐
- 关于HTML5中的sessionStorage的会话级缓存使用
sessionStorage作为HTML5的Web Storage的两种存储方式之一. 用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问并且当会话结束后数 ...
- How to find SPRO path by t-code name
e.g:OB58 F1->Technical Information Find Table Name->V_T011 3.SM30 V_T011 Customizing Continue ...
- Rappid 消除试用版的弹出框
今天想学习JavaScript的FlowCharts,发现有个Rappid库挺不错的(如下图2所示),下了一个后发现在打开窗口时总是要弹出一个提示框,这严重影响了学习的进度,于是相办法将其去掉吧,按照 ...
- log4j2.xml日志文件设置文件路径
笔者最近的项目里使用了spring,spring通过web.xml配置监听器,在web启动时web.root系统变量,以供其他变量使用,例如 在属性文件里使用${web.root}以取得完整路径,项目 ...
- 分割list,将集合按规定个数分为n个部分。
/** * 按指定大小,分隔集合,将集合按规定个数分为n个部分 * * @param list * @param len * @return */ public static <T> Li ...
- 关于接口测试工具postman与DHC介绍
一.Postman背景介绍 用户在开发或者调试网络程序或者是网页B/S模式的程序的时候是需要一些方法来跟踪网页请求的,用户可以使用一些网络的监视工具比如著名的Firebug等网页调试工具.今天给大家介 ...
- oracle12C安装步骤
首先去官网下载两个架包链接如下:官网链接 第一步:将两个架包解压到同一个database目录下.如截图所示: 第二步:打开setup应用程序 打开后就到了下面这个页面 第三步:配置安全更新 环境变量配 ...
- [转]windows中断与共享的连接(samba)
问题:window下当成功登录到samba服务器上的共享的目录的时候,若要是再系想登录此服务器上另外一个共享目录时,会弹出登录窗口. 但是不管输入的用户名和密码对错都会提示. “不允许一个用户使用一个 ...
- 使用signalr实现网页和微信公众号实时聊天(上)
最近项目中需要实现客户在公众号中和客服(客服使用后台网站系统)进行实时聊天的功能.折腾了一段时间,实现了这个功能.现在将过程记录下,以便有相同需求的同行可以参考,也是自己做个总结.这篇是上,用手机编辑 ...
- springBoot 使用拦截器 入坑
近期使用SpringBoot 其中用到了拦截器 结果我的静态资源被全部拦截了,让我导致了好久才搞好: 看下图项目结构: 问题描述:上图划红框的资源都被拦截器给拦截了,搞得项目中不能访问:解决问题就是在 ...