数据结构2

不正常团伙

有n个人站成一行,每个人有一个魅力值,相同魅力值的人会形成一个团伙,定义一个团伙正常当且仅当团伙人数为2,有m个询问:[l,r]中的人组成团伙后,处于不正常团伙的人的魅力值之和。

$n,m,a_{i}<=10^{5}$

题解

这道题用莫队很好去维护信息,开一个桶就好了。就是一些特判:当前转移后到达2个,转移前是2个。

#include<ctime>
#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn=100005;
int n,m,size;
int nowl,nowr;
int a[maxn],pos[maxn];
ll nowans,ans[maxn];
int cnt[maxn];
struct question{
    int l,r,id;
}q[maxn];

template<class T>inline void read(T &x){
    x=0;int f=0;char ch=getchar();
    while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x = f ? -x : x ;
}

bool cmp(question a,question b){
    if(pos[a.l]!=pos[b.l]) return pos[a.l]<pos[b.l];
    return a.r<b.r;
}

void add(int x){
    cnt[a[x]]++;
    nowans+=a[x];
    if(cnt[a[x]]==2) nowans-=2*a[x];
    else if(cnt[a[x]]==3) nowans+=2*a[x];
}

void del(int x){
    cnt[a[x]]--;
    nowans-=a[x];
    if(cnt[a[x]]==2) nowans-=2*a[x];
    else if(cnt[a[x]]==1) nowans+=2*a[x];
}

int main(){
    freopen("abnormal.in","r",stdin);
    freopen("abnormal.out","w",stdout);
    read(n);read(m);
    size=sqrt(n+0.5);
    for(int i=1;i<=n;i++){
        read(a[i]);
        pos[i]=(i-1)/size+1;
    }
    for(int i=1;i<=m;i++){
        read(q[i].l);read(q[i].r);
        q[i].id=i;
    }
    sort(q+1,q+m+1,cmp);
    nowl=1;nowr=0;
    for(int i=1;i<=m;i++){
        while(nowr<q[i].r) add(++nowr);
        while(nowl>q[i].l) add(--nowl);
        while(nowr>q[i].r) del(nowr--);
        while(nowl<q[i].l) del(nowl++);
        ans[q[i].id]=nowans;
    }
    for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
}
abnormal

出现次数的东西也可以用线段树维护,离线将询问按照右端点排序,在左端点储存答案,

先求出每个位置前面和他相同的位置,考虑将i这个位置加入后的影响。

[pre[i]+1,i]这个区间a[i]只有一个,所以都加上a[i]。

[pre[pre[i]]+1,pre[i]]这个区间有两个a[i],所以减去a[i]。

[pre[pre[pre[i]]]+1,pre[pre[i]]]这个区间之前有两个,所以加上3*a[i]

对于剩下的前面区间就直接加上[I]即可。

所以区间修改,单点查询

寒假yyr day10 couple(码风很难受)

 

#include<bits/stdc++.h>
using namespace std;

#define ls rt<<1
#define rs rt<<1|1
#define mid ((tr[rt].l+tr[rt].r)>>1)

const int maxn=1e5+5;
const int maxm=1e5+2;
int n,q;
int a[maxn],pos[maxn],pre[maxn];
int used[maxn];
long long ans[maxm];
long long lazy[maxn<<2];
struct tree{
    int l,r;
}tr[maxn<<2];
struct qu{
    int x,y,id;
}que[maxm];

bool cmp(qu a,qu b){
    return a.y<b.y;
}

template<class T>inline void read(T &x){
  x=0;char ch=getchar();
  while(!isdigit(ch))  {ch=getchar();}
  while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
}

void build(int rt,int l,int r){
    tr[rt].l=l;tr[rt].r=r;
    if(l==r)return ;
    build(ls,l,mid);
    build(rs,mid+1,r);
}

void push_down(int rt){
    lazy[ls]+=lazy[rt];
    lazy[rs]+=lazy[rt];
    lazy[rt]=0;
}

void modify(int rt,int l,int r,int val){
    if(l<=tr[rt].l&&tr[rt].r<=r){
       lazy[rt]+=val;
       return ;
    }
    if(lazy[rt]) push_down(rt);
    if(l<=mid) modify(ls,l,r,val);
    if(mid<r) modify(rs,l,r,val);
}

long long query(int rt,int po){
    if(tr[rt].l==tr[rt].r) return lazy[rt];
    if(lazy[rt]) push_down(rt);
    if(po<=mid) return query(ls,po);
    else return query(rs,po);
}

int main(){
    freopen("couple.in","r",stdin);
    freopen("couple.out","w",stdout);
    read(n);read(q);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<=n;i++){//找前面第一个与它相同的 
        pre[i]=pos[a[i]];
        pos[a[i]]=i;
    }
    //for(int i=1;i<=n;i++)  printf("%d ",pre[i]);
    build(1,1,n);
    for(int i=1;i<=q;i++){
        read(que[i].x);
        read(que[i].y);
        que[i].id=i;
    }
    sort(que+1,que+q+1,cmp);
    //for(int i=1;i<=q;i++)  printf("%d %d ",que[i].x,que[i].y);
    int j=1;
    for(int i=1;i<=n;i++){//移动右端点 
        modify(1,pre[i]+1,i,a[i]);//此区间只有一个a[i] 
        if(pre[i]){
            modify(1,pre[pre[i]]+1,pre[i],-a[i]);//此区间a[i]数量变为2 
        }
        if(pre[pre[i]]){
            modify(1,pre[pre[pre[i]]]+1,pre[pre[i]],3*a[i]);//此区间a[i]数量为3,之前该区间a[i]和为0,现不满足恰好两个,一共要加3个 
        }
        if(pre[pre[pre[i]]]){
            modify(1,1,pre[pre[pre[i]]],a[i]);//此区间数量简单增加就好 
        }
        while(j<=q&&que[j].y==i){
          ans[que[j].id]=query(1,que[j].x);
          j++;
      }
      if(j>q) break;
    }
    for(int i=1;i<=q;i++)  printf("%I64d\n",ans[i]);
}
/*
5 5
4 3 3 4 1
1 1
1 4 
1 2
1 5
2 3
*/
abnormal

 


不正常国家

给出一颗n个点的有根树,根为1。每个点有权值,每个点的答案就是他所管辖的子树中,经过他的路径上所有点的异或和 的最大值。

输出每个点的答案。

$n<=10^{5},a_{i}在int范围$

题解

暴力就是先求出每个点到根的前缀异或和o,再枚举两个点,他们对他们lca的贡献就是(o[x]^o[y]^a[lca]),因为是点权所以要把抵掉的a[lca]异或回来。$O(n^{2}logn)$

用欧拉序求lca可以降到$O(n^{2})$,考试忘了是哪种欧拉序了。

 

参考

参考2

考虑能不能利用01trie弄出来。对于当前节点x,先将他的一颗子树的01trie建出来(对于o值),然后对于剩下的每个子树先查询再丢入01trie(查询是带入o[y]^a[x]),最后对于根节点要先丢值进去再查询(答案可能就是这个点的点权,特判或者这样搞都行)。

对于每个节点都重新建01trie显然不行,有些信息是可以共用的,所以这个问题就又变成了树上启发式合并。

对于每个节点,先把轻儿子的答案找出来,然后删除建出的01trie,接着把重儿子的答案找出来。这个时候重儿子的01trie是可以利用的,他就相当于刚才讲的第一颗子树,然后再按照刚才说的去用轻儿子子树查询和建树。

需要注意的是查询和建树不能写在一个函数,不然得到的答案就可能是同一个儿子所在子树。

当然删除也不能直接memset,dfs跑一遍01trie回收节点即可。

#include<queue>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxn=100005;
int n,cnt,head[maxn];
int a[maxn],o[maxn],size[maxn],son[maxn];
int ret,fa[maxn],ans[maxn];
int go[maxn*40][2];
queue<int> q;
struct edge{
    int x,y,next;
}e[maxn<<1];

template<class T>inline void read(T &x){
    x=0;int f=0;char ch=getchar();
    while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x = f ? -x : x ;
}

void add_edge(int x,int y){
    e[++cnt]=(edge){x,y,head[x]};
    head[x]=cnt;
}

void dfs(int x){
    size[x]=1;
    for(int i=head[x];i;i=e[i].next){
        int y=e[i].y;
        if(y==fa[x]) continue;
        o[y]=o[x]^a[y];
        fa[y]=x;
        dfs(y);
        size[x]+=size[y];
        if(size[y]>size[son[x]]) son[x]=y;
    }
}

int newnode(){
    if(!q.empty()) {int x=q.front();q.pop();return x;}
    return ++cnt;
}

void make(int x){
    int now=1;
    for(int i=1<<30;i;i>>=1){
        bool c=x&i;
        if(!go[now][c]) go[now][c]=newnode();
        now=go[now][c];
    }
}

int find(int x){
    int now=1,res=0;
    for(int i=1<<30;i;i>>=1){
        bool c=x&i;
        if(go[now][c^1]) now=go[now][c^1],res+=i;
        else now=go[now][c];
    }
    return res;
}

void query(int x,int rt){
    for(int i=head[x];i;i=e[i].next){
        int y=e[i].y;
        if(y==fa[x]) continue;
        query(y,rt);
    }
    ret=max(ret,find(o[x]^a[rt]));
}

void add(int x,int rt){
    for(int i=head[x];i;i=e[i].next){
        int y=e[i].y;
        if(y==fa[x]) continue;
        add(y,rt);
    }
    make(o[x]);
}

void clear(int now,int s){
    if(!now) return ;
    if(s==-1) return ;
    clear(go[now][0],s-1);
    clear(go[now][1],s-1);
    go[now][0]=go[now][1]=0;
    if(now!=1) q.push(now);
}

void get_ans(int x){
    if(!x) return ;
    for(int i=head[x];i;i=e[i].next){
        int y=e[i].y;
        if(y==fa[x]||y==son[x]) continue;
        get_ans(y);
        clear(1,30);
    }
    get_ans(son[x]);
    ret=0;
    for(int i=head[x];i;i=e[i].next){
        int y=e[i].y;
        if(y==fa[x]||y==son[x]) continue;
        query(y,x);
        add(y,x);
    }
    make(o[x]);
    ret=max(ret,find(o[x]^a[x]));
    ans[x]=ret;
}

int main(){
    freopen("irregular.in","r",stdin);
    freopen("irregular.out","w",stdout);
    read(n);
    for(int i=1;i<=n;i++) read(a[i]);
    o[1]=a[1];//写不写都可以 
    for(int i=1;i<n;i++){
        int x,y;
        read(x);read(y);
        add_edge(x,y);add_edge(y,x);
    }
    cnt=1;
    dfs(1);
    get_ans(1);
    for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
}
irregular

不正常序列

定义一个不正常数列:

$F_{1}=1$

$F_{I}=(a*M_{i}+b*i+c) mod (10^{9}+7) (i>1)$

其中$M_{i}$是指数列${F_{1},F_{2}...F{i-1}}$的中位数。

规定当有偶数项的时候,中位数为较小的那个。

求$sum\_{i=1}^{n} F_{i}$

题解

难点在于求中位数。

维护两个堆,大根堆和小根堆。

大根堆储存从小到大排名为[1,(n+1)/2]的值,剩下的在小根堆。两者的堆顶连接两个区间

那么大根堆的堆顶就是中位数。

记大根堆堆顶为x,经过计算得出现在插入的数为y。

当y<x时,说明他在前半部分,就加入大根堆;不然加入小根堆。

为了维护中位数一直在大根堆,所以要保持大根堆的个数为(n+1)/2,当大根堆元素多了的时候,就将堆顶丢到小根堆;小根堆元素多了,就丢到大根堆。

这样丢一定是对的,因为大根堆堆顶就是区间里面最大的,丢掉就是将右边界缩小一个。小根堆同理。

#include<ctime>
#include<queue>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int mod=1000000007;
ll a,b,c,ret;
int n,num1,num2;

template<class T>inline void read(T &x){
    x=0;int f=0;char ch=getchar();
    while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x = f ? -x : x ;
}

priority_queue<ll> q;
priority_queue<ll,vector<ll>,greater<ll> > p;

int main(){
    freopen("unnormal.in","r",stdin);
    freopen("unnormal.out","w",stdout);
    read(a);read(b);read(c);read(n);
    ret=1;num1=1;
    q.push(1);
    for(int i=2;i<=n;i++){
        ll x=q.top();
        ll y=(a*x%mod+b*i%mod+c)%mod;
        ret=ret+y;
        if(y<x) q.push(y),num1++;
        else p.push(y),num2++;
        while(num1>(i+1)/2) {x=q.top();q.pop();p.push(x);num1--;num2++;}
        while(num2>i/2) {x=p.top();p.pop();q.push(x);num2--;num1++;}
    }
    printf("%lld",ret);
}
unnormal

求和不取模.......

 

posted @ 2019-09-26 15:40  _JSQ  阅读(170)  评论(0编辑  收藏  举报