1.POJ The merchant
给定一棵树,每个点有一个点权
每次询问(u,v),求从u到v的路径上,a点权值-b点权值最大
要求a点比b点更靠近(或者等于)v
1 ≤ N, wi, Q ≤ 50000
时限3秒
题解:树链剖分,线段树维护最大最小值,以及当前区间答案。
因为树链剖分是从上往下的,所以区间的左右就变成了树的上和下。
然后方向反了调了一晚上QAQ
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
#define v edge[i].to
#define RepG(i,x) for(int i = head[x];~ i;i = edge[i].next)
#define u t[x]
#define o t[y]
#define ulfc t[x << 1]
#define urtc t[x << 1 | 1]
#define lson x << 1,l,mid
#define rson x << 1 | 1,mid + 1,r
#define RT 1,1,n
#define mid (l + r >> 1)
using namespace std;
const int N = 50005;
const int inf = 1 << 29;
int Rank[N],tid[N],sz[N],fa[N],dep[N],top[N],ql,qr,n,m,w[N],head[N],cnt,son[N],tim;
struct Edge{int next,to;}edge[N << 1];
void save(int a,int b)
{
edge[cnt] = (Edge){head[a],b},head[a] = cnt ++;
edge[cnt] = (Edge){head[b],a},head[b] = cnt ++;
}
struct Seg{int mx,mn,ml,mr;Seg(){mx = ml = mr = -inf;mn = inf;}}t[N << 2];
void Upd(int x){
u.mx = max(ulfc.mx,urtc.mx);
u.mn = min(ulfc.mn,urtc.mn);
u.ml = max(ulfc.ml,max(urtc.ml,urtc.mx - ulfc.mn)); //从左开始走
u.mr = max(ulfc.mr,max(urtc.mr,ulfc.mx - urtc.mn)); //从右开始走
}
Seg Merge(Seg lfc,Seg rtc)
{
Seg uu = Seg();
uu.mx = max(lfc.mx,rtc.mx);
uu.mn = min(lfc.mn,rtc.mn);
uu.ml = max(lfc.ml,max(rtc.ml,rtc.mx - lfc.mn)); // 从左开始走
uu.mr = max(lfc.mr,max(rtc.mr,lfc.mx - rtc.mn)); // 从右开始走
return uu;
}
void dfs(int x) // rt
{
sz[x] = 1;
RepG(i,x)
if(v != fa[x]){
fa[v] = x,dep[v] = dep[x] + 1,dfs(v);
sz[x] += sz[v];
if(!son[x] || sz[son[x]] < sz[v])
son[x] = v;
}
}
void dfs(int x,int tp)
{
top[x] = tp,Rank[tid[x] = ++ tim] = x;
if(!son[x])return;
dfs(son[x],tp);
RepG(i,x)if(v != fa[x] && v != son[x])dfs(v,v);
}
Seg Rev(Seg x){swap(x.ml,x.mr);return x;}
void Build(int x,int l,int r)
{
if(l == r){u.ml = u.mr = 0,u.mx = u.mn = w[Rank[l]];return;}
Build(lson),Build(rson);
Upd(x);
}
Seg Qry(int x,int l,int r)
{
if(ql <= l && r <= qr)
return u;
Seg a = Seg(),b = Seg();
if(ql <= mid)a = Qry(lson);
if(mid < qr)b = Qry(rson);
return Merge(a,b);
}
void Query(int x,int y)
{
Seg Qx = Seg(),Qy = Seg();
int cpt = 1;
while(top[x] != top[y])
{
if(dep[top[x]] < dep[top[y]])
{
ql = tid[top[y]],qr = tid[y];
Qy = Merge(Qry(RT),Qy);
y = fa[top[y]];
}
else
{
ql = tid[top[x]],qr = tid[x];
Qx = Merge(Qx,Rev(Qry(RT)));
x = fa[top[x]];
}cpt ++;
}
if(dep[x] > dep[y]){
ql = tid[y],qr = tid[x];
Qx = Merge(Qx,Rev(Qry(RT)));
}
else{
ql = tid[x],qr = tid[y];
Qy = Merge(Qry(RT),Qy);
}
printf("%d\n",Merge(Qx,Qy).ml);
}
int main (){
while(~scanf("%d",&n))
{
memset(head,-1,sizeof(head));
Rep(i,n)scanf("%d",&w[i]);
Rep(i,n - 1){int a,b;scanf("%d%d",&a,&b);save(a,b);}
dfs(1),dfs(1,1);
fa[1] = 1;
Build(RT);
scanf("%d",&m);
Rep(i,m){int a,b;scanf("%d%d",&a,&b);Query(a,b);}
}
return 0;
}
2.HDU Monkey King
有n个点,每个点有一个权值,有m个操作
每次操作,询问u和v所在联通块权值最大的点x,y,输出最大值,然后将x和y的权值减半,将u和v之间连一条边
n,m<=10^5
题解:感觉线段树合并可以搞,但是正解是左偏树(我太弱不会QAQ)
//正在写QAQ
//写完了QAQ
#include <cstdio>
#include <cstring>
#include <algorithm>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
#define RepG(i,x) for(int i = head[x];~ i ;i = edge[i].next)
#define v edge[i].to
#define u t[x]
#define o t[y]
#define lc ch[0]
#define rc ch[1]
#define urtc t[u.rc]
#define ulfc t[u.lc]
using namespace std;
const int N = 100005;
struct LT{int ch[2],w,d;LT(){lc = rc = w = d = 0;}}t[N];
int fa[N],n,m;
int find(int x){return fa[x] == x ? x : fa[x] = find(fa[x]);}
int Merge(int x,int y)
{
if(!x || !y)return x + y;
if(u.w < o.w)swap(x,y);
u.rc = Merge(u.rc,y);
fa[u.rc] = x;
if(urtc.d > ulfc.d)swap(u.lc,u.rc);
if(!u.rc)u.d = 0;
else u.d = urtc.d + 1;
return x;
}
int Del(int x){
fa[u.lc] = u.lc,fa[u.rc] = u.rc;
int y = Merge(u.lc,u.rc);
u.lc = u.rc = u.d = 0;
return y;
}
void solve(int x,int y)
{
u.w /= 2,o.w /= 2;
int l = Del(x),r = Del(y);
l = Merge(l,x),r = Merge(r,y);
l = Merge(l,r);
printf("%d\n",t[l].w);
}
int main ()
{
while(~scanf("%d",&n)){
Rep(x,n)scanf("%d",&u.w),fa[x] = x,u.d = u.rc = u.lc = 0;
scanf("%d",&m);
Rep(i,m)
{
int tx,ty;
scanf("%d%d",&tx,&ty);
int x = find(tx),y = find(ty);
if(x != y)solve(x,y);
else puts("-1");
}
}
return 0;
}
HDU自带多组数据真的烦QAQ
3.弹飞绵羊
有n个节点排成一排,每个节点有一个属性ki(ki>0,ki是整数)
第i个节点可以达到第i+ki个节点,如果i+ki>n,则可以到达终点
两种操作:
1.询问第i个点到达终点需要多少步
2.修改第i个节点的ki值
n<=200000,m<=100000
题解:LCT裸题。
4.Snacks
有一颗n个节点的有根树,每个节点有一个权值,两种操作
1.修改某一个点的权值
2.寻找一条从根出发的路径,路径必须经过x节点,要求权值和最大
1≤n,m≤100000
题解:注意到经过x节点就是经过x的子树任意一点。
dfs序 + 线段树可以搞掉。树链剖分当然更支持了QAQ
主要介绍一下dfs序的使用方法:
首先前缀和。那么对于某个节点的修改,就修改它的子树。
对于某个根到x的询问,那就直接询问st[x] - > ed[x]这一段的最大值。
多组询问什么的忘记清空标记简直不要太爽。
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <cstring>
#include <algorithm>
#define RT 1,1,n
#define Rep(i,n) for(int i = 1;i <= n;i ++)
#define RepG(i,x) for(int i = head[x];~ i;i = edge[i].next)
#define v edge[i].to
#define u t[x]
#define ulfc t[x << 1]
#define urtc t[x << 1 | 1]
#define mid (l + r >> 1)
#define lson x << 1,l,mid
#define rson x << 1 | 1,mid + 1,r
using namespace std;
const int N = 100005;
const long long inf = 1e15;
typedef long long ll;
struct Edge{int next,to; Edge(int next=0, int to=0):next(next), to(to) {}}edge[N << 1];
struct Tree{ll sum,tag;Tree(){sum = tag = 0;}}t[N << 4];
int tid[N],Rank[N],fa[N],ed[N],tim,cnt,head[N],n,m,ql,qr;
ll sum[N],val[N];
void save(int a,int b){edge[cnt] = Edge(head[a],b);head[a] = cnt ++;}
void dfs(int x){Rank[tid[x] = ++ tim] = x;RepG(i,x)if(v != fa[x])fa[v] = x,sum[v] = sum[x] + val[v],dfs(v);ed[x] = tim;}
void Upd(int x){u.sum = max(ulfc.sum,urtc.sum);}
void Dw(int x){if(u.tag)ulfc.sum += u.tag,urtc.sum += u.tag,ulfc.tag += u.tag,urtc.tag += u.tag,u.tag = 0;}
void Build(int x,int l,int r){if(l == r){u.sum = sum[Rank[l]];u.tag = 0;return;}Build(lson),Build(rson),Upd(x),u.tag = 0;}
void Modify(int x,int l,int r,ll tag)
{
Dw(x);
if(l >= ql && r <= qr){u.sum += tag,u.tag += tag;return;}
if(ql <= mid)Modify(lson,tag);
if(mid < qr)Modify(rson,tag);
Upd(x);
}
ll Qry(int x,int l,int r)
{
Dw(x);
if(l >= ql && r <= qr)return u.sum;
ll lx = -inf,rx = -inf;
if(ql <= mid)lx = Qry(lson);
if(mid < qr)rx = Qry(rson);
return Upd(x),max(lx,rx);
}
int main ()
{
int T;
scanf("%d",&T);
Rep(Case,T){
printf("Case #%d:\n",Case);
scanf("%d%d",&n,&m);
memset(head,-1,sizeof(head));
cnt = tim = 0;
Rep(i,n - 1){int a,b;scanf("%d%d",&a,&b);a ++,b ++;save(a,b),save(b,a);}
Rep(i,n)scanf("%lld",&val[i]),sum[i] = val[i];
dfs(1);
Build(RT);
Rep(i,m)
{
int a,b;ll c;
scanf("%d",&a);
if(a & 1 ^ 1)
{
scanf("%d%lld",&b,&c),b ++;
ql = tid[b],qr = ed[b];
ll cc = c - val[b];
val[b] = c;
Modify(RT,cc);
}
else
{
scanf("%d",&b);
b ++;
ql = tid[b],qr = ed[b];
printf("%lld\n",Qry(RT));
}
}
}
return 0;
}
5.【FOTILE模拟赛】L
给定N个正整数
对于一个询问(l,r),你需要求出max(Ai xor Ai+1 xor Ai+2 … xor Aj),其中l<=i<=j<=r
N=12000,M=6000
强制在线
题解:这题好像不是很好想,现在不是很会。
分块应该可以做,然后搞一个可持久化Trie就好了。
6.SDOI2014方伯伯的玉米田
给定n个数,你可以最多进行k次操作
每次操作可以指定一段区间(l,r),将区间内的每个数加1
要求最后的序列的最长非降子序列最长
1 < N < 10000,1 < K ≤ 500,1 ≤ ai ≤5000
时限6秒
题解:
首先肯定是dp对吧。
f[i][j]表示强制第i个,当前还有j次操作的长度。