2019年秋季学期第九周周任务——YCG

周任务

算法任务:

主席树

具体任务:

P1

HDU1394
权值线段树搞一搞:
求序列所有逆序对的个数,在根据首位交换,逆序对在本题中的个数变化N-2*a[i]-1的算式(首位的数放在最后的位置使得(比a[i]大的)逆序对多了N - a[i]个,少了(比a[i]小的)a[i] + 1个),求出min值

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int maxn = 1e6+5;
int Tree[maxn<<2],N,a[maxn];
void Updata(int L,int R,int index,int pos){
    if(L==R){
        Tree[index] += 1;
        return;
    }
    int Mid = (L + R)>>1;
    if(pos<=Mid)Updata(L,Mid,index<<1,pos);
    else Updata(Mid + 1,R,index<<1|1,pos);
    Tree[index] = Tree[index<<1] + Tree[index<<1|1];
}
long long Query(int L,int R,int index,int l,int r){
    if(l<=L&R<=r){
        return Tree[index];
    }
    long long ans = 0;
    int Mid  = (L + R)>>1;
    if(l<=Mid) ans += Query(L,Mid,index<<1,l,r);
    if(Mid<r)  ans += Query(Mid + 1,R,index<<1|1,l,r);
    return ans;
}
int main(){
    while(~scanf("%d",&N)){
    memset(Tree,0,sizeof(Tree));
    int x;
    long long ans = 0;
    for(int i = 1;i <= N;i++){
          scanf("%d",&a[i]);
          ans += Query(1,N,1,a[i]+1,N); 
          Updata(1,N,1,a[i]);      
    }
    long long tool = ans;
    for(int i = 1;i<= N;i++){
        ans += (N - 2*a[i] - 1);
        tool = min(ans,tool);
    }
    printf("%lld\n",tool);
    }
    return 0;
}

POJ2104
主席树模板题没啥讲的

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 5;
int ls[maxn<<5]={0},rs[maxn<<5]={0},sum[maxn<<5]={0},a[maxn],root[maxn],disc[maxn],now = 0;
int BuildTree(int L,int R){
	int pos = ++now;
	if(L==R)return pos;
	int Mid = (L + R)>>1;
	ls[pos] = BuildTree(L,Mid);
	rs[pos] = BuildTree(Mid + 1,R);
	return pos;
}
int Updata(int L,int R,int num,int front){
	int pos = ++now;
	sum[pos] = sum[front] + 1; 
	if(L==R) return pos; 
	ls[pos] = ls[front];rs[pos] = rs[front]; 
	int Mid = (L + R)>>1;
	if(num<=Mid) ls[pos] = Updata(L,Mid,num,ls[front]);
	else rs[pos] = Updata(Mid+1,R,num,rs[front]);
	return pos; 
}
int Query(int L,int R,int K,int li,int ri){
	if(L==R){
	return disc[L];
    }
	int x = sum[ls[ri]] - sum[ls[li]],Mid = (L + R)>>1;
	if(K<=x)return Query(L,Mid,K,ls[li],ls[ri]);
	else return Query(Mid+1,R,K-x,rs[li],rs[ri]);
}
int main(){
	int N,M;
	scanf("%d%d",&N,&M);
	for(int i = 1;i <= N;i++){
		scanf("%d",&a[i]);
		disc[i] = a[i];
	}
	sort(disc + 1,disc + 1 + N);
	int cnt = unique(disc + 1,disc + 1 + N) - disc - 1;
    root[0] = BuildTree(1,cnt);
	for(int i = 1;i <= N;i++){
		int pos = lower_bound(disc + 1,disc + 1 + cnt,a[i]) - disc;
		root[i] =  Updata(1,cnt,pos,root[i-1]); 
	}
	for(int i = 1;i <= M;i++){
		int l,r,k;
		scanf("%d%d%d",&l,&r,&k);
	 int ans = Query(1,cnt,k,root[l-1],root[r]);
	 printf("%d\n",ans);
	}
	return 0;
}

9012牛客多校训练营第九场H
就是主席树+前缀和+二分
代码(自己写的有亿点点bug懒得改了,其他dalao的代码):

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
#include<math.h>
#include<queue>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define me0(a) memset(a,0,sizeof(a))
#define me1(a) memset(a,-1,sizeof(a))
using namespace std;
  
const int maxn = 4e6 + 100;
const int INF = 0x3f3f3f3f;
typedef long long LL;
  
void read(LL& val) { int x = 0; int bz = 1; char c; for (c = getchar(); (c<'0' || c>'9') && c != '-'; c = getchar()); if (c == '-') { bz = -1; c = getchar(); }for (; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - 48; val = x * bz; }
  
LL n, q, a[maxn], b[maxn], sz, tot, root[maxn];
LL ls[maxn], rs[maxn], rt[maxn], val[maxn];
LL s[maxn];//s表示总和
LL sum[maxn];
  
void build(LL& o, int L, int R) {
    o = ++tot;
    val[o] = 0; s[o] = 0;
    if (L == R) return;
    int m = (L + R) >> 1;
    build(ls[o], L, m);
    build(rs[o], m + 1, R);
}
  
void update(LL& o, int L, int R, int last, int p) {//last 指的是上一个root的结点编号
    o = ++tot;
    ls[o] = ls[last];//
    rs[o] = rs[last];
    s[o] = s[last] + p;
    val[o] = val[last] + 1;//表示当前结点的重量增加1
    if (L == R) return;
    int m = (L + R) >> 1;
    if (p <= m) update(ls[o], L, m, ls[last], p);//根据p值判断插入左子树还是右子树中
    else update(rs[o], m + 1, R, rs[last], p);
}
  
int query(int ss, int tt, int L, int R, int k) {//区间起点、终点,查询的第k小
    if (L == R) return L;
    int m = (L + R) >> 1;
    int cnt = val[ls[tt]] - val[ls[ss]];//结点的左子树相减,表示这个期间左子树上增加了多少个新的值,然后常规操作
    if (k <= cnt) return query(ls[ss], ls[tt], L, m, k);//说明第k小在当前m的左边
    else return query(rs[ss], rs[tt], m + 1, R, k - cnt);
  
}
  
LL query2(int ss, int tt, int L, int R, int k) {//查询前k小的和
    if (L == R)  {
        return (LL)k * L;
    }
    int m = (L + R) >> 1;
  
    int cnt = val[ls[tt]] - val[ls[ss]];
    if (k > cnt) {
        return s[ls[tt]] - s[ls[ss]] + query2(rs[ss], rs[tt], m + 1, R, k - cnt);
    }
    else {
        return query2(ls[ss], ls[tt], L, m, k);
    }
  
}
  
void work() {
    LL ql, qr, x, y;
    read(ql); read(qr); read(x); read(y);
    double dpart = double(sum[qr] - sum[ql - 1]) / y * x;//切掉部分的长度和
    LL L = 0, R = qr - ql + 1, m = 0, res = 0;
    LL num = qr - ql + 1;//总共num根竹子
    LL Kth = a[ql], Ksum = 0, K = 0;
    while (L <= R) {
        m = (L + R) >> 1;
        Kth = query(root[ql - 1], root[qr], 1, sz, m);//找到m小,表示切到了第m小
        Ksum = query2(root[ql - 1], root[qr], 1, sz, m);//前m小
        Ksum = sum[qr] - sum[ql - 1] - Ksum - (num - m) * Kth;//切掉的部分
  
        if ((double)Ksum - 0.00000001 >= dpart) {
            res = Kth;
            K = m;
            L = m + 1;
        }
        else {
            R = m - 1;
        }
    }
    double ans = Kth + ((double)Ksum - dpart) / (num - K);
    printf("%.9lf\n", ans);
      
}
  
int main() {
    //freopen("in.txt", "r", stdin);
    read(n); read(q);
    fo(i, 1, n) read(a[i]), sum[i] = sum[i - 1] + a[i], sz = max(sz, a[i]);//这里a[i]是连续的,不连续的需要另外做处理
    tot = 0;
    build(root[0], 1, sz);//首先建立一个空树
  
    fo(i, 1, n) update(root[i], 1, sz, root[i - 1], a[i]);//按输入顺序依次插入到相应的结点中去,并更改途中结点的信息(此时该结点有多少个子节点)
  
    while (q--) work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值