【树状数组】树状数组の奇技淫巧专场

本文介绍了两种非常规形态的树状数组应用案例,包括异或版树状数组解决Nim游戏策略问题及MAX/MIN版树状数组求解最长递增子序列问题。

本篇文章将介绍一些非常规形态的树状数组的使用。

一)异或版

树状数组中记录的是一段值异或的结果。
例题:BZOJ2819
题目大意:
给定一棵树,每个节点是一堆石子,给定两种操作:
1.改变x号节点的石子数量
2.用从x到y的路径上的所有堆石子玩一次Nim游戏,询问是否有必胜策略
题解:
既然它只修改点的话,影响到的只是它这棵子树。那么很容易就想到了dfs序。这个子树就是连续一段。先维护每个点dfs开始时和结束时的时间戳。修改的时候先在它自己的开始、结束位置上xor它自己变成零,然后再修改。(x,y)路径上的xor值=query(x的开始) xor query(y的开始) xor lca(x,y)的点权。很好想通。LCA就倍增算一下好了。
代码如下:

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#define ll long long
#define inf 0x7f7f7f7f
#define N 500005
#define lb(x) (x&-x)
using namespace std;
ll read()
{
    ll x=0,f=1;
    char c=getchar();
    while(c<'0' || c>'9') {if(c=='-') f=-1;c=getchar();}
    while(c<='9' && c>='0') {x=x*10+c-'0';c=getchar();}
    return x*f;
}
int n,m,x,y,v[N],e[N<<1],nex[N<<1],hd[N],tot,ind; 
int t[N],l[N],r[N],fa[N][20],dep[N];
char op[5];
void mdy(int x,int v)
{
    while(x<=n)
    {
        t[x]^=v;
        x+=lb(x);
    }
}
int query(int x)
{
    int ret=0;
    while(x)
    {
        ret^=t[x];
        x-=lb(x);
    }
    return ret;
}
void add(int u,int v)
{
    e[++tot]=v,nex[tot]=hd[u],hd[u]=tot;
    e[++tot]=u,nex[tot]=hd[v],hd[v]=tot;
}
void dfs(int u)
{
    l[u]=++ind;
    for(int i=hd[u];i;i=nex[i])
    {
        if(e[i]==fa[u][0]) continue;
        fa[e[i]][0]=u;
        dep[e[i]]=dep[u]+1;
        dfs(e[i]);
    }
    r[u]=ind;
}
void init()
{
    for(int i=1;i<=19;i++)
    for(int j=1;j<=n;j++) fa[j][i]=fa[fa[j][i-1]][i-1];
}
int lca(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    int t=dep[u]-dep[v];
    for(int i=0;i<=18;i++)
    if((1<<i)&t) u=fa[u][i];
    for(int i=18;i>=0;i--)
    if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
    if(u==v) return u;
    return fa[u][0];
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++) v[i]=read();
    for(int i=1;i<n;i++) add(read(),read());
    dfs(1);init();
    for(int i=1;i<=n;i++) mdy(l[i],v[i]),mdy(r[i]+1,v[i]);
    m=read();
    for(int i=1;i<=m;i++)
    {
        scanf("%s",op);
        x=read(),y=read();
        if(op[0]=='Q')
        {
            int t=lca(x,y);
            int ret=query(l[x])^query(l[y])^v[t];
            if(ret) puts("Yes");
            else puts("No");
        }
        else
        {
            mdy(l[x],v[x]),mdy(r[x]+1,v[x]);
            v[x]=y;
            mdy(l[x],v[x]),mdy(r[x]+1,v[x]);
        }
    }
    return 0;
}

二)MAX、MIN版

树状数组中记录的是一些值的最大值。
例题:BZOJ3594
题解:
令f[i][j]表示前i个数上升j次的最大LIS
那么有f[i][j]=max{f[k][l]|k < i,l <= j,a[k]+l <= a[i]+j}+1
由于dp方程记录的是最大值,因此树状数组也必须相匹配,记录最大值。
这也提示我们树状数组的变化是很灵活的,要根据需要决定。
代码如下:

#include<stdio.h>  
#include<string.h>  
#include<math.h>  
#include<iostream>  
#include<algorithm> 
#define lb(x) (x&(-x)) 
using namespace std;  
int c[6005][505],dp[10005][505],a[10005];  
int n,m,ans,mx;   
void mdy(int x,int y,int z)  
{  
    for(int i=x;i<=mx+m;i+=lb(i))  
    for(int j=y;j<=m+1;j+=lb(j)) c[i][j]=max(c[i][j],z);  
}  
int query(int x,int y)  
{  
    int ret=0;  
    for(int i=x;i;i-=lb(i))  
    for(int j=y;j;j-=lb(j)) ret=max(ret,c[i][j]);  
    return ret;  
}  
int main()  
{  
    scanf("%d%d",&n,&m);  
    ans=0;  
    for(int i=1;i<=n;i++) 
    {
        scanf("%d",&a[i]);
        mx=max(mx,a[i]);
    }  
    for(int i=1;i<=n;i++)  
    for(int j=m;j>=0;j--)  
    {  
        dp[i][j]=query(a[i]+j,j+1)+1;  
        ans=max(ans,dp[i][j]);  
        mdy(a[i]+j,j+1,dp[i][j]);  
    }  
    printf("%d\n",ans);  
    return 0;  
}  
【电动汽车充电站有序充电调度的分散式优化】基于蒙特卡诺和拉格朗日的电动汽车优化调度(分时电价调度)(Matlab代码实现)内容概要:本文介绍了基于蒙特卡洛和拉格朗日方法的电动汽车充电站有序充电调度优化方案,重点在于采用分散式优化策略应对分时电价机制下的充电需求管理。通过构建数学模型,结合不确定性因素如用户充电行为和电网负荷波动,利用蒙特卡洛模拟生成大量场景,并运用拉格朗日松弛法对复杂问题进行分解求解,从而实现全局最优或近似最优的充电调度计划。该方法有效降低了电网峰值负荷压力,提升了充电站运营效率与经济效益,同时兼顾用户充电便利性。 适合人群:具备一定电力系统、优化算法和Matlab编程基础的高校研究生、科研人员及从事智能电网、电动汽车相关领域的工程技术人员。 使用场景及目标:①应用于电动汽车充电站的日常运营管理,优化充电负荷分布;②服务于城市智能交通系统规划,提升电网与交通系统的协同水平;③作为学术研究案例,用于验证分散式优化算法在复杂能源系统中的有效性。 阅读建议:建议读者结合Matlab代码实现部分,深入理解蒙特卡洛模拟与拉格朗日松弛法的具体实施步骤,重点关注场景生成、约束处理与迭代收敛过程,以便在实际项目中灵活应用与改进。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值