Guess the weight 题解(思维+线段树)

题目链接

题目大意

这个题目也太难了把。。

不看代码写,我感觉基本写不出

还有要注意mid需要有时候移位

官方题解有点小问题 分母应该是\(n(n-1)\)

还有一段大佬的话,看起来更好理解

当前一共有 all 个卡牌,分母显然是 all * (all - 1) (就我们只需要考虑排列的前两个数,后面的不影响)然后,对于卡牌 i ,设 <i 的卡牌有 a 张, >i 的卡牌有 b 张,那么卡牌 i 一定挑 a,b 中大的那个方向选,所以可以发现存在一个分界线 x ,使得 <=x 的卡牌按大于它的策略,>x的卡牌按小于它的策略计算分子的话,就考虑卡牌对 (i,j) 对答案的贡献,如果它属于横跨分界线的,则先 i 后 j 和先 j 后 i 都是可行的;如果 (i,j) 在同一块里(即要么 i,j 都 <= x 要么 i,j 都 >x),那么它们先 i 后 j 和 先 j 后 i 中恰好有一个能产生贡献。所以我代码里那个就是 all * (all - 1) / 2 ,这个计算的就是假设 (i,j) 只有一个产生贡献,然后 all * (all - num) 就是跨越块新增的贡献,再减去 ban (指的就是 (i,i) 这种情形多算了,要被减掉)

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define debug printf(" I am here\n");
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll> pii;
mt19937 rnd(time(0));
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=2e5+5,inf=0x3f3f3f3f,mod=20071027;
const double eps=1e-10;
int n,q;
ll sz;
ll a[maxn];
ll tree1[maxn<<2],tree2[maxn<<2];
void push(int node){
    tree1[node]=tree1[node<<1]+tree1[node<<1|1]+tree2[node<<1]*tree2[node<<1|1];
    tree2[node]=tree2[node<<1]+tree2[node<<1|1];
}
void build(int node,int l,int r){
    if(l==r){
        tree1[node]=a[l]*a[l];
        tree2[node]=a[l];
        return ;
    }
    int mid=(l+r)/2;
    build(node<<1,l,mid);
    build(node<<1|1,mid+1,r);
    push(node);
}
void update(int node,int l,int r,int pos,int add){
    if(l==r){
        tree2[node]+=add;
        tree1[node]=tree2[node]*tree2[node];
        return ;
    }
    int mid=(l+r)/2;
    if(mid>=pos) update(node<<1,l,mid,pos,add);
    else  update(node<<1|1,mid+1,r,pos,add);
    push(node);
}
pii query_ans(int node,int L,int R,int l,int r){
    if(L>r||R<l){
        return {0,0};
    }
    if(L<=l&&r<=R){
        return {tree1[node],tree2[node]};
    }
    int mid=(l+r)/2;
    pii sum={0,0},temp1={0,0},temp2={0,0};
    if(mid>=L) temp1=query_ans(node<<1,L,R,l,mid);
    if(mid<R)  temp2=query_ans(node<<1|1,L,R,mid+1,r);
    sum={temp1.fi+temp2.fi+temp1.se*temp2.se,temp1.se+temp2.se};
    return sum;
}
int query_mid(int node,int l,int r,int sz){// 线段树上二分找分界点
    if(l==r){
        return l;
    }
    int mid=(l+r)/2,ans=-1;
    if(tree2[node<<1]>=sz) ans=query_mid(node<<1,l,mid,sz);
    else ans=query_mid(node<<1|1,mid+1,r,sz-tree2[node<<1]);
    return ans;
}
signed main(){
    int _;scanf("%d",&_);
    while(_--){
        for(int i=1;i<=200000;i++){
            a[i]=0;
        }
        scanf("%d%d",&n,&q);
        for(int i=1,x;i<=n;i++){
            scanf("%d",&x);
            a[x]++;
        }
        sz=n;
        build(1,1,200000);
        for(int i=1,opt,pos,add;i<=q;i++){
            scanf("%d",&opt);
            if(opt==1){
                scanf("%d%d",&add,&pos);
                sz+=add;
                update(1,1,200000,pos,add);
            }else{
                int mid=query_mid(1,1,200000,sz/2);
                ll sum=query_ans(1,1,mid,1,200000).se,val=query_ans(1,mid,mid,1,200000).se;
                if(sum-val>=sz-sum) mid--;
                pii a=query_ans(1,1,mid,1,200000);
                pii b=query_ans(1,mid+1,200000,1,200000);
                // 去重自己不能选择自己
                a.fi-=a.se;
                b.fi-=b.se;

                ll mu=sz*(sz-1);
                ll zi=mu-a.fi-b.fi;
                ll g=__gcd(mu,zi);
                printf("%lld/%lld\n",zi/g,mu/g);
            }
        }
    }
    return 0;
}
posted @ 2021-08-19 10:46  hunxuewangzi  阅读(65)  评论(0编辑  收藏  举报