[여름방학 특집 훈련 \ # 데이터 구조]

HDU 2492 Ping pong (트 리 배열 + 역순 수 2008 Regional Beijing)
[제목 링크]: 여 기 를 클릭 하 세 요 ~ ~
[제목 부주의]
모든 사람 에 게 실력 치 랭 킹 이 있 습 니 다. 순 서 는 바로 그의 위치 입 니 다. 가장 많은 경 기 를 요구 합 니 다. 경 기 는 두 명의 동료, 한 명의 심판 을 요구 하고 심판 의 실력 을 그들 사이 에 요구 합 니 다. 그리고 위 치 는 그들 두 사람의 중간 에 있어 야 합 니 다. 이렇게 하면 한 경 기 를 구성 할 수 있 습 니 다.-- 총 몇 경 기 를 조직 할 수 있 나.
[사고]: 이 문 제 는 트 리 배열 (BIT) 을 사용 해 야 합 니 다.
BIT 는 이러한 데이터 구조 이다.
주어진 초기 값 이 0 인 수열 a1, a2...
1: 주어진 i: 계산 a1 + a2 +. ai;
2: 주어진 i 와 x: 실행 ai + = x
나무 모양 배열 을 처음 만 들 었 을 때 는 아주 오래 전 ~ ~ ~, 아직도 쓰기 에 익숙 하지 않 습 니 다. 나무 모양 배열 이 실현 할 수 있 는 기능 선분 나 무 는 기본적으로 모두 실현 할 수 있 지만, 반대로 꼭 그렇지 는 않 습 니 다. 하지만 나무 모양 배열 의 효율 이 비교적 높다 고 합 니 다. BIT 운용 번호 의 이 진 표 시 는 구간 과 매우 쉽게 대응 할 수 있 기 때 문 입 니 다.따라서 이 성질 을 이용 하여 간단 한 비트 연산 으로 실현 한다.
돌아 와 서 이 문 제 를 보 세 요. 만약 에 a [i] 가 자신 을 심판 으로 한 다음 에 왼쪽 에 몇 명 이 자신 보다 작은 지 찾 아 보 세 요. 그리고 오른쪽 에 몇 명 이 자신 보다 큰 지 찾 아 보 세 요. 그리고 두 개의 숫자 를 곱 하 는 것 은 바로 그 를 심판 으로 하 는 경기 장 수 입 니 다. 물론 반대 도 있 습 니 다. 오른쪽 이 자신 보다 작은 것 을 찾 고 왼쪽 이 자신 보다 큰 것 을 찾 아 곱 하 는 것 입 니 다.
얼마나 작 거나 큰 개 수 를 빨리 찾 으 려 면 BIT 의 역할 을 해 야 합 니 다.
정의:
LL left_low[N],left_big[N]; LL right_low[N],right_big[N];
그러면 정 답 은...  res+=left_low[i]*right_big[i]+left_big[i]*right_low[i];
코드:
#include <bits/stdc++.h>
using namespace std;
const int N=100005;
typedef long long LL;

int lowbit(int x){return x&(-x);}
LL num[N];
LL bit[N];
LL left_low[N],left_big[N];
LL right_low[N],right_big[N];
LL n;

LL sum(int i){
    LL s=0;
    while(i>0){
        s+=bit[i];
        i-=lowbit(i);
    }
    return s;
}

void add(int i,int x){
    while(i<=N){
        bit[i]+=x;
        i+=lowbit(i);
    }
}

int main(){
    int t;scanf("%d",&t);
    while(t--){
        scanf("%lld",&n);
        for(int i=1; i<=n; ++i){
            scanf("%lld",&num[i]);
        }
        LL res=0;
        memset(bit,0,sizeof(bit));
        for(int i=1; i<=n; ++i){
            add(num[i],1);
            left_big[i]=sum(N)-sum(num[i]);
            left_low[i]=sum(num[i]-1);
        }
        memset(bit,0,sizeof(bit));
        for(int i=n; i>=1; --i){
            add(num[i],1);
            right_big[i]=sum(N)-sum(num[i]);
            right_low[i]=sum(num[i]-1);
        }
        //  left_lower[i]:    i   num[i]      
        //  n-i-right_lower[i]:num[i]         num[i]      
        //  right_lower[i]:    i   num[i]      
        //  i-left_lower[i]-1:num[i]         num[i]      
        for(LL i=1; i<=n; ++i){
            //res+=(left_lower[i]*(n-i-right_lower[i])+right_lower[i]*(i-left_lower[i]-1));
           res+=left_low[i]*right_big[i]+left_big[i]*right_low[i];
        }
        printf("%lld
",res); } return 0; }

HDU 1806   빈번 한 값 (이산 화 + RMQ)
[제목 링크]: 여 기 를 클릭 하 세 요 ~ ~
[제목 부주의]:
 길이 가 N (N < = 100000) 인 단조 로 운 하강 서열 Si 를 정 하고, 그 다음 에 Q (Q < = 100000) 조 에 게 주어진 구간 에 가장 많은 수의 횟수 가 나타 나 는 지 물 어보 세 요.
[사고방식]: 이것 은 사 고 를 비교적 고찰 하 는 문제 입 니 다. 
Q 가 매우 크기 때문에 문의 의 복잡 도 는 반드시 log (n) 가 있어 야 한다. 그러면 먼저 선분 트 리 라 는 것 을 확인한다. 이 문 제 는 제한 조건 이 있 고 모든 수 는 단 조 롭 고 증가 하지 않 는 배열 이다. 다시 말 하면 두 수가 같다 면 그들 사이 의 모든 수 는 반드시 그들 과 같다 는 것 이다.그래서 O (Q log (n)) (RMQ 를 사용 하면 O (Q) 의 알고리즘 이 생 겼 습 니 다.
모든 수 에 대해 하나의 그룹 번 호 를 설정 하면 이산 화 라 고도 할 수 있 습 니 다. 같은 수 는 같은 그룹 번 호 를 가 진 다음 에 각 수의 빈 도 를 한 배열 에 기록 하여 이런 수의 크기 를 나타 내 고 입력 에 대한 문의 [x, y] 는 그들 이 어느 그룹 에 있 는 지 직접 조회 하고 세 가지 상황 으로 나 누 어 토론 합 니 다.
   1) 만약 x 와 y 가 한 그룹 에 있다 면 가장 긴 길 이 는 y - x + 1 이다.
   2) 그룹 번호 가 1 차이 가 나 면 이들 의 분계 점 z (z 점 과 x 점 의 조합 번호 가 같다 고 가정) 를 찾 아 라. 그러면 답 은 Max {z - x + 1, y - z} 이다.
   3) 만약 에 차이 가 1 보다 크 면 먼저 두 끝 을 자 르 고 큰 기록 을 통계 한 다음 에 중간 부분의 최대 치 와 크기 를 비교 하면 중간 부분의 최대 치 는 선분 트 리 를 사용 할 수 있다. 혹은 RMQ 구간 조회 최 값.
코드 (RMQ):
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int N=2e5+10;
LL n,q;
LL bit[N],cnt[N];
LL num[N];
LL dpmax[N][20];///max
LL dpmin[N][20];///min

struct node    ///         
{
    int beg,end;
}line[N];

void getline(){ ///         ,      ,“   ”     //O(N)
    memset(cnt,0,sizeof(cnt));///      
    int k=1;int tmp=num[1];
    num[1]=1;cnt[1]=1;
    line[1].beg=1;
    for(int i=2; i<=n; ++i){
        if(num[i]!=tmp){/// appear the new sequence
            tmp=num[i];
            line[k].end=i-1;
            num[i]=++k;
            cnt[k]++;
            line[k].beg=i;
        }
        else{ ///tmp==num[i]
            num[i]=k;
            cnt[k]++;
        }
    }
    line[k].end=n;
}

void RMQ_init(){ ///RMQ
    for(int i=1; i<=n; ++i) dpmax[i][0]=cnt[i];
    double limit=log(n)/log(2.0);
    for(int j=1; j<=(int)limit; ++j)
        for(int i=1; i+(1<<j)-1<=n; ++i){
            dpmax[i][j]=max(dpmax[i][j-1],dpmax[i+(1<<(j-1))][j-1]);
           // dpmin[i][j]=min(dpmin[i][j-1],dpmin[i+(1<<(j-1))][j-1]);
        }
}

LL getmax(LL a,LL b){
    LL k=(int)(log(b-a+1)/log(2.0));
    return max(dpmax[a][k],dpmax[b-(1<<k)+1][k]);
}
LL mid_max;

int main()
{
    while(scanf("%lld",&n)!=EOF&&n){
        scanf("%lld",&q);
        for(int i=1; i<=n; ++i) scanf("%lld",&num[i]);
        getline();
        RMQ_init();
        while(q--){
            LL a,b;
            scanf("%lld%lld",&a,&b);
            if(num[a]==num[b]) printf("%lld
",b-a+1); else if(num[a]==num[b]-1){ printf("%lld
",max(line[num[a]].end-a+1,b-line[num[a]].end)); } else{ //a,b 1 mid_max=getmax(num[a]+1,num[b]-1);// printf("%lld
",max(mid_max,max(line[num[a]].end-a+1,b-line[num[b]].beg+1))); } } } return 0; }

POJ 2431 Expediton  (욕심 + 우선 대기 열)
[제목 링크]: 여 기 를 클릭 하 세 요 ~ ~
[제목 부주의]:
트럭 한 대가 L 단위 의 거 리 를 운행 해 야 한다.처음에는 트럭 에 P 단위 의 휘발유 가 있 었 고 앞으로 1 단 위 를 달 릴 때마다 휘발유 1 단 위 를 소모 했다.도중에 차 의 휘발유 가 다 떨 어 지면 트럭 은 더 이상 갈 수 없고 결승점 에 도착 할 수 없다.도중에 모두 N 개의 주유소 가 있 는데 주유소 가 제공 하 는 유량 이 제한 되 어 있 고 트럭 의 오 일 탱크 는 무 한 히 커서 아무리 기름 을 넣 어도 문제 가 없다.각 주유소 의 종점 거리 와 제공 할 수 있 는 유량 을 제시 하고 트럭 에 출발점 에서 종점 까지 적어도 몇 번 의 기름 을 넣 어야 하 느 냐 고 물 었 다.종점 에 도착 하지 못 하면 출력 - 1.[사고방식]: N 이 비교적 크기 때문에 효율 적 인 해법 을 찾 아야 합 니 다.트럭 이 종점 으로 가 는 도중에 주유소 에서 만 기름 을 넣 을 수 있다.하지만 '주유소 i 에 도 착 했 을 때 그 이후 언제든지 비 단위 휘발 유 를 넣 을 수 있 는 권 리 를 한 번 얻 었 다' 고 생각하면 문제 해결 에 도 마찬가지다.이후 주유 가 필요 할 때 는 앞서 지나 간 주유소 에 기름 을 넣 었 다 고 생각하면 된다.주유 횟수 가 가능 한 한 적 었 으 면 좋 겠 기 때문에 연료 가 0 이 되면 다시 주유 하 는 것 이 최선 이다.욕심 에 따라 연료 가 0 일 때 가장 많은 주유 소 를 선택한다.따라서 지나 가 는 주유소 의 유량 을 우선 대기 열 로 저장 할 수 있 으 며, 주유 가 필요 할 때 대기 열 에 있 는 최대 요 소 를 꺼 내 면 된다.
#include <stdio.h>
#include <string.h>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1000005;

struct node{
    int dist,val;/// the dist and the mount of oil
    bool operator < (const node &a) const{
    return dist<a.dist;
    }
}line[N];
int n,l,p;

void solve(){
    int ans=0,pos=0,tank=p;///the primary oil
    priority_queue <int >val;
    for(int i=0; i<=n; ++i){
        int d=line[i].dist-pos; ///
        while(tank<d){
            if(val.empty()){
                puts("-1");
                return;
            }
            tank+=val.top();
            val.pop();
            ans++;
        }
        tank-=d;
        pos=line[i].dist;
        val.push(line[i].val);
    }
    printf("%d
",ans); } void init(){ scanf("%d",&n); for(int i=0; i<n; ++i) scanf("%d %d",&line[i].dist,&line[i].val); scanf("%d%d",&l,&p); for(int i=0; i<n; ++i) line[i].dist=l-line[i].dist; line[n].dist=l;line[n].val=0; sort(line,line+n+1); } int main(){ init(); solve(); return 0; } /* Sample Input 4 4 4 5 2 11 5 15 10 25 10 Sample Output 2 */

POJ 3253  Fence Repair  욕심 + 우선 대기 열
[제목 링크]: 여 기 를 클릭 하 세 요 ~ ~
[제목 대의]: 아주 긴 널 빤 지 를 N 조각 으로 자 르 고 자 르 려 고 하 는 길 이 는 각각 L1, L2, L3 이다. 매번 에 널 빤 지 를 자 르 는 비용 은 원래 널빤지 의 길이 의 합 이다. 예 를 들 어 길이 21 의 널 빤 지 는 5, 8, 8, 3 조각 으로 자 르 고 비용 은 21, 13 이 며 마지막 비용 은 21 + 13 = 34 로 최소 비용 을 써 야 한다.
[사고]: 널빤지 의 집합 에서 가장 짧 은 두 조각 을 꺼 내 고 길 이 를 두 개의 널빤지 와 합 친 판 자 를 집합 에 넣 으 면 되 기 때문에 우선 대기 열 로 실현 할 수 있 습 니 다.
코드:
#include <stdio.h>
#include <queue>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=20005;
typedef long long LL;
LL num[N];
LL n;
void solve(){
    priority_queue <int ,vector<int >,greater<int > >val;
    while(!val.empty()) val.pop();
    for(int i=1; i<=n; ++i) val.push(num[i]);
    LL ans=0;
    while(val.size()>1){
        int l1=val.top();val.pop();
        int l2=val.top();val.pop();
        int l3=l1+l2;ans+=(l3);
        val.push(l3);
    }
    printf("%lld
",ans); } void init(){ scanf("%lld",&n); for(int i=1; i<=n; ++i) scanf("%lld",&num[i]); } int main(){ init(); solve(); return 0; }

욕심 + 우선 대기 열

좋은 웹페이지 즐겨찾기