洛谷P2590:
题意:
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。
我们将以下面的形式来要求你对这棵树完成一些操作:
I. CHANGE u t : 把结点u的权值改为t
II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值
III. QSUM u v: 询问从点u到点v的路径上的节点的权值和
注意:从点u到点v的路径上的节点包括u和v本身
思路:裸的树链剖分,这里我用了最常用的轻重链剖分
轻重链剖分:
我们记为以i为根的树的节点个数,v为i的儿子,那么最大的v,我们称为重儿子,从i到重儿子的边叫做重边,其余叫轻边,把所有的重边剖分为一条链,那么整棵树被我们剖分成了好几条链,对于每条链,我们都用线段树去维护,这样就把树的操作转化为了对好几条链的操作
特点:思路好想,但代码沉长,容易错
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=30100;
struct node{
ll next,to;
}e[N<<1];ll h[N],tot;
inline void add(ll a,ll b){
e[++tot]=(node){h[a],b};h[a]=tot;
e[++tot]=(node){h[b],a};h[b]=tot;
}
ll sze[N],son[N],top[N],dfscnt;
ll dep[N],rev[N],seg[N],fa[N];
//fa[i]表示i的父亲,sze[i]表示以i为根的子树大小
//dep[i]表示i的深度,top[i]表示i所在链的开头
//son[i]表示i的重儿子,seg[i]表示i的dfs序
//rev[i]表示dfs序为i的节点,即rev[seg[i]]=i
void dfs1(ll u,ll f){
dep[u]=dep[f]+1;
fa[u]=f;sze[u]=1;
ll maxsize=-1,heavyson=0;
for(ll i=h[u];i;i=e[i].next){
register ll v=e[i].to;
if (v==f) continue;dfs1(v,u);
if (sze[v]>maxsize){
maxsize=sze[v];
heavyson=v;
}
sze[u]+=sze[v];
}
son[u]=heavyson;
}
void dfs2(ll u,ll Top){
if (son[u]){
seg[son[u]]=++dfscnt;
rev[dfscnt]=son[u];
top[son[u]]=Top;
dfs2(son[u],Top);
}
else return;
for(ll i=h[u];i;i=e[i].next){
register ll v=e[i].to;
if (v!=fa[u]&&v!=son[u]){
seg[v]=++dfscnt;
rev[dfscnt]=v;
top[v]=v;dfs2(v,v);
}
}
}
ll sum[N<<2],Max[N<<2],a[N];
//这三个数组都是线段树用的
inline void pushup(ll o){
sum[o]=sum[o<<1]+sum[o<<1|1];
Max[o]=max(Max[o<<1],Max[o<<1|1]);
}
void build(ll o,ll l,ll r){
if (l==r){
sum[o]=a[rev[l]];
Max[o]=a[rev[r]];
return;
}
ll mid=(l+r)>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
pushup(o);return;
}
void update(ll o,ll l,ll r,ll p,ll v){
if (l==r&&r==p){
sum[o]=Max[o]=v;
return;
}
if (l>p||r<p) return;
register ll mid=(l+r)>>1;
if (p<=mid) update(o<<1,l,mid,p,v);
if (mid<p) update(o<<1|1,mid+1,r,p,v);
pushup(o);return;
}
ll querymax(ll o,ll l,ll r,ll p,ll q){
// printf("querymax(%d,%d,%d,%d,%d)\n",o,l,r,p,q);
if (p<=l&&r<=q) return Max[o];
if (l>q||r<p) return -1000;
register ll ans=-1000,mid=(l+r)>>1;
if (p<=mid) ans=max(ans,querymax(o<<1,l,mid,p,q));
if (mid<q) ans=max(ans,querymax(o<<1|1,mid+1,r,p,q));
return ans;
}
ll querysum(ll o,ll l,ll r,ll p,ll q){
if (p<=l&&r<=q) return sum[o];
if (l>q||r<p) return 0;
register ll ans=0,mid=(l+r)>>1;
if (p<=mid) ans+=querysum(o<<1,l,mid,p,q);
if (mid<q) ans+=querysum(o<<1|1,mid+1,r,p,q);
return ans;
}
#define gc getchar()
#define g(c) isdigit(c)
inline ll read(){
ll x=0;char c=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}
ll n,m,i,x,y;string s;
inline ll QMAX(ll x,ll y){
register ll ans=-1000;
while (top[x]!=top[y]){
if (dep[top[x]]<dep[top[y]]) swap(x,y);
ans=max(ans,querymax(1,1,n,seg[top[x]],seg[x]));
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
return max(ans,querymax(1,1,n,seg[x],seg[y]));
}
inline ll QSUM(ll x,ll y){
register ll ans=0;
while (top[x]!=top[y]){
if (dep[top[x]]<dep[top[y]]) swap(x,y);
ans+=querysum(1,1,n,seg[top[x]],seg[x]);
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
return ans+querysum(1,1,n,seg[x],seg[y]);
}
int main(){
freopen("t1.in","r",stdin);
n=read();
for(i=1;i<n;i++)
add(read(),read());
// printf("1\n");
for(i=1;i<=n;i++)
a[i]=read();
seg[1]=rev[1]=1;
top[1]=dfscnt=1;
// printf("2\n");
dfs1(1,0);//printf("3\n");
dfs2(1,1);//printf("4\n");
// for(i=1;i<=n;i++){
// printf("top[%d]=%d\n",i,top[i]);
// }
build(1,1,n);m=read();
// printf("5\n");
for(i=1;i<=m;i++){
cin>>s;x=read();y=read();
// cout<<s<<" "<<x<<" "<<y<<endl;
if (s.find('X')!=s.npos){
if (x>y) swap(x,y);
printf("%d\n",QMAX(x,y));
}
else if (s.find('S')!=s.npos){
if (x>y) swap(x,y);
printf("%d\n",QSUM(x,y));
}
else update(1,1,n,seg[x],y);
// printf("-----i=%d-----\n",i);
}
return 0;
}
洛谷P1247:
题意:
输入k及k个整数n1,n2,…,nk,表示有k堆火柴棒,第i堆火柴棒的根数为ni;接着便是你和计算机取火柴棒的对弈游戏。取的规则如下:每次可以从一堆中取走若干根火柴,也可以一堆全部取走,但不允许跨堆取,也不允许不取。
谁取走最后一根火柴为胜利者。
编一个程序,在给出初始状态之后,判断是先取必胜还是先取必败,如果是先取必胜,请输出第一次该如何取。如果是先取必败,则输出“lose”。
思路:很经典的博弈论的题目,先手必胜当且仅当n[1]^n[2]^n[3]^...^n[k],其中^表示异或,然后当n[i]^(n[1]^n[2]^n[3]^...^n[k])<n[i]时,我们就在n[i]中取n[i]-(n[i]^(n[1]^n[2]^n[3]^...^n[k]))个石子即可
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+3e2;
int a[N],ans,i,j,n;
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
char c=0;int x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}
//数据量太大,打个快读
bool first=true;
int main(){
freopen("t1.in","r",stdin);
n=read();
for(i=1;i<=n;i++){
a[i]=read();
ans^=a[i];
}
if (ans==0) printf("lose");//先手必败
else{
for(i=1;i<=n;i++)
if ((ans^a[i])<a[i]){
int p=ans^a[i];
printf("%d %d\n",a[i]-p,i);
for(j=1;j<=n;j++){
if (first) first=false;
else printf(" ");
if (j==i) printf("%d",p);
else printf("%d",a[j]);
}
return 0;//这个return 0很重要,否则你的评测将五彩缤纷!
}
}
return 0;
}