UNR#3 Day1——[ 堆+ST表+复杂度分析 ][ 结论 ][ 线段树合并 ]
第一题是鸽子固定器。
只会10分。按 s 从小到大排序,然后 dp[ i ][ j ][ k ] 表示前 i 个元素、已经选了 j 个、最小值所在位置是 k 的最大代价。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
ll Mx(ll a,ll b){return a>b?a:b;}
ll Mn(ll a,ll b){return a<b?a:b;}
const int N=,M=;
int n,m,ds,dv,f[M][N]; ll ans=-1e18;
struct Node{
int s,v;
bool operator< (const Node &b)const
{return s<b.s;}
}a[N];
ll calv(int x)
{ if(dv==)return x;return (ll)x*x;}
ll cals(int x)
{ if(ds==)return x;return (ll)x*x;}
int main()
{
n=rdn();m=rdn();ds=rdn();dv=rdn();
for(int i=;i<=n;i++)
a[i].s=rdn(),a[i].v=rdn();
sort(a+,a+n+);
for(int i=;i<=n;i++)
{
for(int j=Mn(i,m);j>;j--)
for(int k=;k<i;k++)
{
ans=Mx(ans,calv(f[j-][k]+a[i].v)-cals(a[i].s-a[k].s));
f[j][k]=Mx(f[j][k],f[j-][k]+a[i].v);
}
f[][i]=a[i].v;
ans=Mx(ans,calv(a[i].v));
}
printf("%lld\n",ans);
return ;
}
囧
当 ds=dv=1 的时候,不用考虑最小值是多少,只要考虑新增一个元素的代价。也就是原来的最后一个元素不是最大值、自己才是最大值。
那么 \( dp[i][j] \) 表示前 i 个选 j 个、一定选了第 i 个;这样就知道第 i 个是当前的最后一个。
转移只需 \( dp[i][j]=v[i]+s[i]+\max\limits_{1<=k<i} dp[k][j-1]-s[k] \) ;前缀最大值优化 DP 。没有实现。
应该考虑不仅用 DP 来做。
注意到一个暴力做法:确定 r ,枚举 l ;那么中间选择的一定是 v 最大的一些元素。那么维护对于 v 的小根堆,看看 l 能否取代堆顶即可。
1.一旦 r 被弹出堆,就直接枚举下一个 r ; 2.用 ST 表来二分寻找下一个 l (下一个 v 大于堆顶的元素)。
根据题解的证明,这样复杂度是 O(nmlogn) ,可过。虽然还可以做到 O(nm) ,但没有实现。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
ll Mx(ll a,ll b){return a>b?a:b;}
ll Mn(ll a,ll b){return a<b?a:b;}
const int N=2e5+,K=;
int n,m,ds,dv,lg[N],bin[K],mx[N][K]; ll ans;
struct Node{
int s,v;
bool operator< (const Node &b)const
{return s<b.s;}
}a[N];
priority_queue<int,vector<int>,greater<int> > q;
ll cal(int v,int s){return (dv==?v:(ll)v*v)-(ds==?s:(ll)s*s);}
int main()
{
n=rdn();m=rdn();ds=rdn();dv=rdn();
for(int i=;i<=n;i++)
a[i].s=rdn(), a[i].v=rdn();
for(int i=;i<=n;i++)lg[i]=lg[i>>]+;
bin[]=;for(int i=;i<=lg[n]+;i++)bin[i]=bin[i-]<<;//+1
sort(a+,a+n+);
for(int i=;i<=n;i++)
{
mx[i][]=a[i].v;
for(int t=;i>=bin[t];t++)
mx[i][t]=Mx(mx[i][t-],mx[i-bin[t-]][t-]);
}
for(int i=,j;i<=n;i++)
{
while(q.size())q.pop(); int sm=;
for(j=i;j&&j>i-m;j--)
{
q.push(a[j].v);sm+=a[j].v;
ans=Mx(ans,cal(sm,a[i].s-a[j].s));
}
while()
{
int d=q.top(); if(d==a[i].v)break;
for(int t=lg[j];t>=;t--)
if(bin[t]<=j&&mx[j][t]<=d)j-=bin[t];//bin[t]<=j
if(!j)break;
q.pop(); q.push(a[j].v); sm+=a[j].v-d;
ans=Mx(ans,cal(sm,a[i].s-a[j].s));
j--;/////
}
}
printf("%lld\n",ans);
return ;
}
第二题是 To Do Tree 。
每次把新解锁的点放进堆里,每次操作取出向下的 dep 前 m 大的元素即可。不太会证明……
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
int Mx(int a,int b){return a>b?a:b;}
int Mn(int a,int b){return a<b?a:b;}
const int N=1e5+;
int n,m,hd[N],xnt,to[N<<],nxt[N<<];
int dep[N],p[N],tot,ct[N],cnt;
struct cmp{
bool operator() (const int &a,const int &b)const
{return dep[a]<dep[b];}
};
priority_queue<int,vector<int>,cmp> q;
void add(int x,int y)
{
to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;
}
void dfs(int cr)
{
for(int i=hd[cr],v;i;i=nxt[i])
{
dfs(v=to[i]); dep[cr]=Mx(dep[cr],dep[v]+);
}
}
int main()
{
n=rdn();m=rdn();
for(int i=,d;i<=n;i++)
{ d=rdn();add(d,i);}
dfs(); q.push();
while(q.size())
{
cnt++; int lst=tot;
while(q.size())
{
p[++tot]=q.top(); q.pop();
if(tot-lst==m)break;
}
ct[cnt]=tot-lst;
for(int i=lst+;i<=tot;i++)
for(int j=hd[p[i]];j;j=nxt[j])
q.push(to[j]);
}
printf("%d\n",cnt);
for(int i=,nw=;i<=cnt;i++)
{
printf("%d ",ct[i]);
for(int j=;j<=ct[i];j++,nw++)
printf("%d ",p[nw]); puts("");
}
return ;
}
第三题是配对树。
不知道怎么不枚举序列上的区间。
考虑 m2 枚举区间。把区间里的元素“加入”表示树上该点到根的链上点的状态都取反。树点的状态为 0/1 表示其子树里有 偶数/奇数 个区间里的点;如果状态是 1 的话,该点连向父亲的边需要加入答案。
用 LCT 维护这个过程。区间 (L-2,R) 可以继承 (L,R) 的状态。应该是 O( m2logn ) 的,但是在链的数据上实测很慢。不 makeroot 而每次 access 的 LCT 复杂度?总之得了20分。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define ls c[x][0]
#define rs c[x][1]
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
const int N=1e5+,mod=;
int upt(int x){while(x>=mod)x-=mod;while(x<)x+=mod;return x;} int n,m,a[N],hd[N],xnt,to[N<<],nxt[N<<],w[N<<],ans,prn;
int fa[N],c[N][],vl[N],sm[N],s2[N]; bool tg[N],b[N];
bool rv[N];
int sta[N],top;
void add(int x,int y,int z)
{ to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;w[xnt]=z;}
void dfs(int cr,int f)
{
fa[cr]=f;
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=f)
{
vl[v]=sm[v]=w[i]; dfs(v,cr);
}
}
bool isrt(int x){return c[fa[x]][]!=x&&c[fa[x]][]!=x;}
void pshd(int x)
{
if(rv[x])
{
rv[x]=; if(ls)rv[ls]^=; if(rs)rv[rs]^=; swap(ls,rs);
}
if(!tg[x])return; tg[x]=;
if(ls) s2[ls]=upt(sm[ls]-s2[ls]), tg[ls]^=, b[ls]^=;
if(rs) s2[rs]=upt(sm[rs]-s2[rs]), tg[rs]^=, b[rs]^=;
}
void pshp(int x)
{
sm[x]=upt(sm[ls]+sm[rs]); sm[x]=upt(sm[x]+vl[x]);
s2[x]=upt(s2[ls]+s2[rs]); if(b[x])s2[x]=upt(s2[x]+vl[x]);
}
void rotate(int x)
{
int y=fa[x],z=fa[y]; bool d=(x==c[y][]);
if(!isrt(y))c[z][y==c[z][]]=x;
fa[x]=z; fa[y]=x; fa[c[x][!d]]=y;
c[y][d]=c[x][!d]; c[x][!d]=y;
pshp(y); pshp(x);
}
void splay(int x)
{
sta[top=]=x;
for(int k=x;fa[k];k=fa[k])sta[++top]=fa[k];
for(int i=top;i;i--)pshd(sta[i]);
for(int y,z;!isrt(x);rotate(x))
{
y=fa[x];z=fa[y];
if(!isrt(y))
((x==c[y][])^(y==c[z][]))?rotate(x):rotate(y);
}
}
void access(int x)
{
for(int t=;x;t=x,x=fa[x])
{
splay(x);//tg[x]=0
c[x][]=t; pshp(x);
}
}
void mkrt(int x)
{
access(x); splay(x); rv[x]^=; swap(ls,rs);
}
void cz(int x)
{
if(rand()&)
{
mkrt(); access(x); splay(x);
int y=s2[x];
s2[x]=upt(sm[x]-s2[x]); tg[x]^=; b[x]^=;
ans=upt(ans+s2[x]-y);
}
else
{
mkrt(x); access(); splay();
int y=s2[];
s2[]=upt(sm[]-s2[]); tg[]^=; b[]^=;
ans=upt(ans+s2[]-y);
}
}
int main()
{
srand();
n=rdn();m=rdn();
for(int i=,u,v,w;i<n;i++)
{
u=rdn();v=rdn();w=rdn()%mod;
add(u,v,w); add(v,u,w);
}
for(int i=;i<=m;i++)a[i]=rdn();
dfs(,);
for(int i=;i<=m;i++)
{
for(int j=i;j>;j-=)
{
cz(a[j]); cz(a[j-]); prn=upt(prn+ans);
}
ans=; for(int j=;j<=n;j++)s2[j]=, tg[j]=b[j]=;
}
printf("%d\n",prn);
return ;
}
囧
不枚举序列上的区间的方法就是直接考虑一条树边被算了几次。
把一个点的子树里的节点对应的序列上的位置都赋值为1。有多少个 “长度为偶数且包含了奇数个1” 的区间,该点连向父亲的边就被算了多少次。
因为是把子树里的点的位置都赋值为1,所以考虑线段树合并。
又注意到长度为偶数的区间 ( L, R ) ,R 和 L-1 的奇偶性相同;所以分开考虑序列的奇数位置和偶数位置,维护“前缀和为奇数/偶数”的个数即可。区间合并的时候看左区间若有奇数个1,就需要把右区间的奇/偶翻转再加到自己身上。
注意动态开点的话,pshp 的时候小心没有左/右孩子的情况。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
#define ls Ls[cr]
#define rs Rs[cr]
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
const int N=1e5+,M=N*,mod=;
int upt(int x){while(x>=mod)x-=mod;while(x<)x+=mod;return x;}
void Add(int &x,int y){x=upt(x+y);} int n,m,hd[N],xnt,to[N<<],nxt[N<<],w[N<<],ans;
int rt[N],tot,Ls[M],Rs[M],sm[M],ct[M][];
vector<int> ps[N];
void add(int x,int y,int z)
{ to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;w[xnt]=z;}
int cal(bool fx,int l,int r)
{
if(!fx)return (r>>)-((l-)>>);//if l==0 then +1
return ((r+)>>)-(l>>);
}
void pshp(int cr,int l,int mid,int r)
{
sm[cr]=sm[ls]+sm[rs];
for(int i=;i<=;i++)ct[cr][i]=ct[ls][i];
if(sm[ls]&)
for(int i=;i<=;i++)
Add(ct[cr][i],cal(i,mid+,r)-ct[rs][i]);//-ct...
else
for(int i=;i<=;i++)
Add(ct[cr][i],ct[rs][i]);
}
void mrg(int l,int r,int &cr,int pr)
{
if(!cr){cr=pr;return;} if(!pr)return;
int mid=l+r>>;
mrg(l,mid,ls,Ls[pr]); mrg(mid+,r,rs,Rs[pr]);
pshp(cr,l,mid,r);
}
void mdfy(int l,int r,int &cr,int p)
{
if(!cr) cr=++tot;
if(l==r)
{
if(l&)ct[cr][]++; else ct[cr][]++;
sm[cr]=; return;
}
int mid=l+r>>;
if(p<=mid)mdfy(l,mid,ls,p); else mdfy(mid+,r,rs,p);
pshp(cr,l,mid,r);
}
void dfs(int cr,int fa,int tw)
{
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=fa)
{
dfs(v,cr,w[i]); mrg(,m,rt[cr],rt[v]);
}
for(int i=,lm=ps[cr].size();i<lm;i++)
mdfy(,m,rt[cr],ps[cr][i]);
int r=rt[cr];
ans=(ans+(ll)ct[r][]*(cal(,,m)-ct[r][])%mod*tw)%mod;
ans=(ans+(ll)ct[r][]*(cal(,,m)-ct[r][])%mod*tw)%mod;
}
int main()
{
n=rdn();m=rdn();
for(int i=,u,v,z;i<n;i++)
{
u=rdn();v=rdn();z=rdn();
add(u,v,z);add(v,u,z);
}
for(int i=,d;i<=m;i++)
{ d=rdn();ps[d].push_back(i);}
dfs(,,); printf("%d\n",ans);
return ;
}
UNR#3 Day1——[ 堆+ST表+复杂度分析 ][ 结论 ][ 线段树合并 ]的更多相关文章
- BZOJ_2006_[NOI2010]超级钢琴_贪心+堆+ST表
BZOJ_2006_[NOI2010]超级钢琴_贪心+堆+ST表 Description 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的 音乐 ...
- [NOIP2016 DAY1 T2]天天爱跑步-[差分+线段树合并][解题报告]
[NOIP2016 DAY1 T2]天天爱跑步 题面: B[NOIP2016 DAY1]天天爱跑步 时间限制 : - MS 空间限制 : 565536 KB 评测说明 : 2s Description ...
- 【BZOJ4919】[Lydsy六月月赛]大根堆 线段树合并
[BZOJ4919][Lydsy六月月赛]大根堆 Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切 ...
- Bzoj 2006: [NOI2010]超级钢琴 堆,ST表
2006: [NOI2010]超级钢琴 Time Limit: 20 Sec Memory Limit: 552 MBSubmit: 2222 Solved: 1082[Submit][Statu ...
- P2048 [NOI2010]超级钢琴 [堆+st表]
考虑只能取长度为 [L,R] 的,然后不难想到用堆搞. 搞个前缀和的st表,里面维护的是一个 最大值的位置 struct rmq { int mx[N][20] ; void qwq(int n) { ...
- 【NOI2010】超级钢琴 题解(贪心+堆+ST表)
题目链接 题目大意:求序列内长度在$[L,R]$范围内前$k$大子序列之和. ---------------------- 考略每个左端点$i$,合法的区间右端点在$[i+L,i+R]$内. 不妨暴力 ...
- [BZOJ3277/BZOJ3473] 串 - 后缀数组,二分,双指针,ST表,均摊分析
[BZOJ3277] 串 Description 现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串(注意包括本身). Solution 首先将所有串连 ...
- [NOI2010] 超级钢琴 - 贪心,堆,ST表
这也算是第K大问题的套路题了(虽然我一开始还想了个假算法),大体想法就是先弄出最优的一批解,然后每次从中提出一个最优解并转移到一个次优解,用优先队列维护这个过程即可. 类似的问题很多,放在序列上的,放 ...
- LOJ #6041. 「雅礼集训 2017 Day7」事情的相似度 LCT+SAM+线段树
Code: #include<bits/stdc++.h> #define maxn 200003 using namespace std; void setIO(string s) { ...
随机推荐
- ubuntu/如何启动、关闭和设置ubuntu防火墙
由于LInux原始的防火墙工具iptables过于繁琐,所以ubuntu默认提供了一个基于iptable之上的防火墙工具ufw. ubuntu 9.10默认的便是UFW防火墙,它已经支持界面操作了.在 ...
- django 的 MTV 流程图
- MySQL的事务和视图
事务 1.概念 一条或者多条sql语句的集合! 事务:就是一堆操作的集合,他们同生共死.要么都执行成功,要么都执行失败2.事务的特性 ACID A:原子性 完整的,不可分割的 原子性 (Atom ...
- oracle跟SQL Server 2005 的区别
Oracle与Sql server的区别 一直搞不明白Oracle数据库和sql server的区别,今天我特意查资料把他们的区别整理出来 Oracle数据库:Oracle Database,又名 ...
- 小白学Python——用 百度翻译API 实现 翻译功能
本人英语不好,很多词组不认识,只能借助工具:百度翻译和谷歌翻译都不错,近期自学Python,就想能否自己设计一个百度翻译软件呢? 百度翻译开放平台: http://api.fanyi.baidu.co ...
- stdcall 函数调用过程(以delphi为例),还有负数的补码
以delphi下调用stdcall 函数为例,从右往左压栈:procedure TForm1.Button2Click(Sender: TObject);var i:integer;begin i:= ...
- TApplication,TForm,TControl,TComponent,TWinControl研究(博客索引)good
TApplication,TForm,TControl,TComponent,TWinControl研究 http://blog.csdn.net/suiyunonghen/article/detai ...
- 自动化部署三剑客 gitlab + ansible + jenkins
http://www.showerlee.com/archives/1880 https://edu.51cto.com/center/course/lesson/index?id=280700 Gi ...
- 2019-8-31-MobaXterm-使用代理
title author date CreateTime categories MobaXterm 使用代理 lindexi 2019-08-31 16:55:58 +0800 2018-02-13 ...
- Codeforces 939 时区模拟 三分
A #include <bits/stdc++.h> #define PI acos(-1.0) #define mem(a,b) memset((a),b,sizeof(a)) #def ...