zoukankan      html  css  js  c++  java
  • 数据结构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
    ",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
    ",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
    ",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

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

  • 相关阅读:
    [慢查优化]慎用MySQL子查询,尤其是看到DEPENDENT SUBQUERY标记时
    Web开发基本准则-55实录-缓存策略
    Web开发基本准则-55实录-Web访问安全
    线上Java应用排查和诊断规范
    [慢查优化]建索引时注意字段选择性 & 范围查询注意组合索引的字段顺序
    [慢查优化]联表查询注意谁是驱动表 & 你搞不清楚谁join谁更好时请放手让mysql自行判定
    再说memcache的multiget hole(无底洞)
    RCA:未注意Curl-library Post 1024以上字节时的HTTP/1.1特性导致 HessianPHP 传输数据失败
    (研发系)职业化7个细节
    5·12和6·17两知名网站域名被劫持事件实施过程回放
  • 原文地址:https://www.cnblogs.com/sto324/p/11592191.html
Copyright © 2011-2022 走看看