最近半期考试比较忙,因此部分代码没有写,以后补上。
优势人群(Efficient Solutions, UVa11020)
description
给出(n) 个物品,每个物品有两个特征值(L,C) 。定义物品(i) 比物品(j) 优秀当且仅当满足以下条件至少一条
- (L_i<L_j) 且 (C_ile C_j)
- (L_ile L_j) 且 (C_i< C_j)
称一个物品是优秀的当且仅当不存在其他物品比它优秀。
现在要求每加入一个物品,输出有多少物品是优秀的。
solution
如果我们每个物品(i) 看作平面上的点,坐标为((L_i,C_i)) ,那么任意时刻所有优秀的物品的图像如下图所示
即随着(L) 的递增,(C) 单调递减。并且可以发现如果某个物品在某时刻不优秀,那么在之后的时刻它都不可能优秀。
考虑加入一个新的物品。
- 这个物品本身不优秀:考虑左边与它相邻的点的纵坐标是否比它小。
- 这个物品本身优秀:加入集合中。考虑所有在它右边的点,看是否会变得不优秀。如果是,直接删除。
具体实现可以用(multiset) 。
每个元素最多被加入一次,删除一次,因此总复杂度(mathcal O(nlog n))
code
#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef pair<int,int> pii;
multiset<pii>s;
int n;
inline void ins(pii x)
{
auto t=s.upper_bound(x);
if(t==s.begin())s.insert(x);
else
{
t--;auto p=*t;
if((p.fi<x.fi&&p.se<=x.se)||p.se<x.se)return;
s.insert(x);
}
while(1)
{
t=s.upper_bound(x);
if(t==s.end())break;
if((*t).se>=x.se)s.erase(t);
else break;
}
}
int main()
{
int T,kase=0;scanf("%d",&T);
while(1)
{
scanf("%d",&n);
printf("Case #%d:
",++kase);
while(n--)
{
int x,y;scanf("%d%d",&x,&y);
ins(make_pair(x,y));
printf("%u
",s.size());
}
s.clear();
if(kase<T)puts("");
else break;
}
return 0;
}
异象石(SDOI2015)
description
大小为N的一棵树,M个时刻发生M个事件,类型有三:
1.某个点出现异象石。
2.删除异象石。
3.询问问异象石所在的点连通边集的总长度最小是多少。
data range
(Nle10^5)
solution
此题思路很妙啊
考虑按照(dfs) 序维护所有异象石,设(sum) 为相邻两个异象石的距离之和(首尾也算),答案就是(dfrac {sum} 2)
具体来说
- 插入(x)
(sum) 减去(dis(pre,nxt)) ,再加上(dis(pre,x)+dis(x,nxt))
- 删除(x)
(sum) 减去(dis(pre,x)+dis(x,nxt)) ,再加上(dis(pre,nxt))
具体维护用(set) 就可以了,但要特殊处理首尾相邻的情况
这道题还用到了不少关于(set) 的技巧,值得积累
time space complexity
时间&空间:(O(nlog n))
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,H=17;
int n,dfn[N],dfc,sign,fa[N][20],dep[N];
ll len[N];
int tin[N],tout[N];
struct edge{int to,w;edge(int _to=0,int _w=0){to=_to,w=_w;}};
struct cmp{bool operator()(const int&x,const int&y){return dfn[x]<dfn[y];}};
set<int,cmp>s;
vector<edge>e[N];
void dfs(int u,int pr)
{
tin[u]=++sign,dfn[u]=++dfc;
fa[u][0]=pr,dep[u]=dep[pr]+1;
for(int i=1;(1<<i)<=dep[u];++i)
fa[u][i]=fa[fa[u][i-1]][i-1];
for(size_t i=0;i<e[u].size();++i)
{
edge v=e[u][i];
if(v.to==pr)continue;
len[v.to]=len[u]+1ll*v.w;
dfs(v.to,u);
}
tout[u]=++sign;
}
inline bool isac(int x,int y){return tin[x]<=tin[y]&&tout[y]<=tout[x];}
inline int lca(int x,int y)
{
if(dep[x]>dep[y])swap(x,y);
if(isac(x,y))return x;
for(int i=H;i>=0;--i)
if(!isac(fa[x][i],y))
x=fa[x][i];
return fa[x][0];
}
inline ll dis(int x,int y)
{
int LCA=lca(x,y);
return len[x]+len[y]-2*len[LCA];
}
inline int pre(int x)
{
auto it=s.lower_bound(x);
if(it==s.begin())return *s.rbegin();
it--;return *it;
}
inline int nxt(int x)
{
auto it=s.lower_bound(x);
it++;if(it==s.end())return *s.begin();
return *it;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;++i)
{
int x,y,z;scanf("%d%d%d",&x,&y,&z);
e[x].push_back(edge(y,z));
e[y].push_back(edge(x,z));
}
len[1]=0;dfs(1,0);tin[0]=0,tout[0]=++sign;
int m;scanf("%d",&m);
char ch[5];int x;ll ans=0;
while(m--)
{
scanf("%s",ch);
if(ch[0]=='?')printf("%lld
",ans>>1);
else
{
scanf("%d",&x);
if(ch[0]=='+')
{
s.insert(x);
int l=pre(x),r=nxt(x);
ans-=dis(l,r);
ans+=dis(l,x)+dis(x,r);
}
else
{
int l=pre(x),r=nxt(x);
ans-=dis(l,x)+dis(x,r);
ans+=dis(l,r);
s.erase(x);
}
}
}
return 0;
}
图询问(Graph and Queries, Tianjin 2010, HDU3726 )
solution
删边显然难以维护。于是根据套路,将所有操作离线下来,倒序处理,就可以转删边为加边了。
计算第(k) 大权值是基础操作,不再赘述。
修改(X) 的权值为(V) 可以看作先将其原来的权值删除,再插入权值(V) 。
重点在于合并。这里采用启发式合并以维护复杂度。即对于两棵平衡树,我们总是会将小的合并到大的中。可以证明这样做会带来(O(log n)) 的复杂度,因为考虑每一个点,每一次合并时如果被加入了较大集合中,那么子树大小必然翻倍,而最大也就到(n) ,因此得证。
总复杂度(mathcal O(nlog^2n)) 。
Permutation Transformer, UVa11922
solution
平衡树模板题目,主要为了熟悉代码。
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
mt19937 rd(time(0));
namespace fhq_Treap
{
int rt,tot,sz[N],id[N],ls[N],rs[N],fix[N];bool tag[N];
inline int nd(int x)
{
int p=++tot;id[p]=x,sz[p]=1;
fix[p]=rd();return p;
}
inline void upd(int x){sz[x]=sz[ls[x]]+sz[rs[x]]+1;}
inline void cov(int x){swap(ls[x],rs[x]);}
inline void down(int x)
{
if(!tag[x])return;
tag[ls[x]]^=1,tag[rs[x]]^=1;
cov(ls[x]),cov(rs[x]);
tag[x]=0;
}
void split(int p,int d,int&l,int&r)
{
if(!p){l=r=0;return;}
down(p);
if(sz[ls[p]]+1<=d)
{
l=p;
split(rs[p],d-1-sz[ls[p]],rs[l],r);
}
else
{
r=p;
split(ls[p],d,l,ls[r]);
}
upd(p);
}
int merge(int x,int y)
{
if(!x||!y)return x^y;
down(x),down(y);
if(fix[x]>fix[y])
{
rs[x]=merge(rs[x],y),upd(x);
return x;
}
else
{
ls[y]=merge(x,ls[y]),upd(y);
return y;
}
}
inline void work(int l,int r)
{
int a=0,b=0,c=0;
split(rt,l-1,a,b);
split(b,r-l+1,b,c);
tag[b]^=1,cov(b);
rt=merge(merge(a,c),b);
}
inline void print(int p)
{
down(p);
if(ls[p])print(ls[p]);
printf("%d
",id[p]);
if(rs[p])print(rs[p]);
}
}
using namespace fhq_Treap;
int n,q;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)rt=merge(rt,nd(i));
scanf("%d",&q);
while(q--)
{
int l,r;scanf("%d%d",&l,&r);
work(l,r);
}
print(rt);
return 0;
}