顯示具有 Zerojudge 標籤的文章。 顯示所有文章
顯示具有 Zerojudge 標籤的文章。 顯示所有文章

2017年4月20日 星期四

(Zerojudge) b332: NOIP2013 3.小朋友的数字

https://zerojudge.tw/ShowProblem?problemid=b332

有點小小的陷阱QQ

當初我想說,寫一個線段樹求區間連續最大和,開long long,結果一直WA最後兩筆。

仔細想想,最後有可能爆long long!!!!  QQ (想想全部都是10^9的case !!!)

附上悲慘的code:

#include <iostream>
#include <stdio.h>
using namespace std;

typedef __int128 LL;
const int MAX_N = 1e6 + 6;
const LL INF = 1e17 + 6;

struct Meruru {
    LL sum;
    LL lmax,rmax,midmax;
    void shootingstars(LL _val) {
        sum=lmax=rmax=midmax=_val;
    }
};

Meruru empty={0,-INF,-INF,-INF};

bool operator==(const Meruru &m1,const Meruru &m2) {
    return m1.lmax==m2.lmax && m1.rmax==m2.rmax && m1.midmax==m2.midmax && m1.sum == m2.sum;
}

Meruru pull(Meruru m1,Meruru m2) {
    Meruru meruru;
    if (m1 == empty) return m2;
    if (m2 == empty) return m1;
    meruru.sum = m1.sum+m2.sum;
    meruru.lmax = max(m1.lmax,m1.sum+m2.lmax);
    meruru.rmax = max(m2.rmax,m2.sum+m1.rmax);
    meruru.midmax = max(max(m1.midmax,m2.midmax),m1.rmax+m2.lmax);
    return meruru;
}

struct Node {
    Node *lc,*rc;
    Meruru meruru;
    Node() {
        lc=rc=NULL;
        meruru = empty;
    }
};

LL a[MAX_N];

Node* Build(int L,int R) {
    Node* node = new Node();
    if (L==R) {
        node->meruru.shootingstars(a[L]);
        return node;
    }
    int mid=(L+R)>>1;
    node->lc=Build(L,mid);
    node->rc=Build(mid+1,R);
    node->meruru = pull(node->lc->meruru,node->rc->meruru);
    return node;
}

Meruru query(Node* node,int L,int R,int l,int r) {
    if (l>R || L>r) return empty;
    else if (l<=L && R<=r) return node->meruru;
    int mid=(L+R)>>1;
    return pull(query(node->lc,L,mid,l,r),query(node->rc,mid+1,R,l,r));
}

LL p[MAX_N];
LL pp[MAX_N];

int main () {
    int n,mod;
    scanf("%d %d",&n,&mod);
    for (int i=1;n>=i;i++) {
        long long x;
        scanf("%lld",&x);
        a[i]=x;
//        scanf("%lld",&a[i]);
    }
    Node* root=Build(1,n);
    for (int i=1;n>=i;i++) {
        Meruru meruru=query(root,1,n,1,i);
        p[i] = meruru.midmax;
    }
    pp[1] = p[1];
    LL mx=pp[1]+p[1];
    for (int i=2;n>=i;i++) {
        pp[i] = mx;
        mx = max(mx,pp[i] + p[i]);
    }
    mx = pp[1];
    for (int i=1;n>=i;i++) {
        mx = max(mx,pp[i]);
    }
    if (mx<0) {
        printf("%d\n",int(LL(mx)%mod));
    }
    else printf("%d\n",int(mx%mod));
}

2017年4月18日 星期二

(Zerojudge) b412: 【記憶中】之記憶中的并查集 [可持久化並查集]

https://zerojudge.tw/ShowProblem?problemid=b412

使用黑魔法rope XD

其實可用可持久化線段樹

有啟發式合併 + 路徑壓縮XD


#include <iostream>
#include <stdio.h>
#include <ext/rope>
using namespace std;
using namespace __gnu_cxx;

const int MAX_N = 1e5 + 6;

rope<int> *p[MAX_N],*sz[MAX_N];
int pp[MAX_N],szz[MAX_N];

int Find(int ver,int x) {
    int ret;
    if (p[ver]->at(x) == x) return x;
    ret = Find(ver,p[ver]->at(x));
    if (p[ver]->at(x) == ret) return ret;
    p[ver]->replace(x,ret);
    return ret;
}

void Union(int ver,int x,int y) {
    x = Find(ver,x);
    y = Find(ver,y);
    if (x==y) return;
    if (sz[ver]->at(x) > sz[ver]->at(y)) {
        sz[ver]->replace(x,(sz[ver]->at(x) + sz[ver]->at(y)));
        p[ver]->replace(y,x);
    }
    else {
        sz[ver]->replace(y,(sz[ver]->at(x) + sz[ver]->at(y)));
        p[ver]->replace(x,y);
    }
}

int main () {
    int n,m;
    scanf("%d %d",&n,&m);
    int ans=0;
    for (int i=0;n>=i;i++) {
        pp[i] = i;
        szz[i] = 1;
    }
    n+=3;
    p[0] = new rope<int>(pp,pp+n+1);
    sz[0]= new rope<int>(szz,szz+n+1);
    for (int i=1;m>=i;i++) {
        int a;
        scanf("%d",&a);
        a^=ans;
        if (a==0) {  //go to history version
            int b;
            scanf("%d",&b);
            b^=ans;
            p[i] = p[b];
            sz[i]=sz[b];
        }
        else if (a==1) {
            int b,c;
            scanf("%d %d",&b,&c);
            p[i] = new rope<int>(*p[i-1]);
            sz[i]= new rope<int>(*sz[i-1]);
            b^=ans;
            c^=ans;
            Union(i,b,c);
        }
        else {
            int b,c;
            scanf("%d %d",&b,&c);
            p[i] = new rope<int>(*p[i-1]);
            sz[i]= new rope<int>(*sz[i-1]);
            b^=ans;
            c^=ans;
            ans = (Find(i,b) == Find(i,c));
            printf("%d\n",ans);
        }
    }
}


(Zerojudge) d539: 區間 MAX [終極sparse-table]

https://zerojudge.tw/ShowProblem?problemid=d539

花O(n lg lg n)預處理,花O(1)查詢任意區間的RMQ。

想法:

把lgN東西分成一塊,對每一塊做Sparse-Table,
總複雜度為O( N/(lgN) * lgN * lg(lgN) ) = O(N lg lg N)

再對每一塊的Max做Sparse-Table : O( (N/lgN) * lg(N/lg(N)) ) = O(N)

之後就可以O(1)查詢。想想看吧!

//use sparse table + magic to <O(n lg lg n),O(1)>
#include <iostream>
#include <stdio.h>
#include <cmath>
using namespace std;

const int MAX_N = 5e5 +60;
const int MAX_P = 22;
const int MAX_NN = 19;  //lg N
const int MAX_MM = 5;
const int MAX_NNN = 30000+6;
const int MAX_MMM = 16; //lg NN
const int _INF = -2147483648;

int a[MAX_N];
int st_small[MAX_NNN][MAX_MM][MAX_NN];
int st_big[MAX_MMM][MAX_NNN];
int lg2[MAX_N];
int pow2[MAX_P];

void make_st_small(int n,int *a,int st[MAX_MM][MAX_NN]) {
    for (int i=0;n>i;i++) {
        st[0][i] = a[i];
    }
    for (int i=1;MAX_MM>i;i++) {
        for (int j=0;n>j;j++) {
            int r=j+pow2[i-1];
            if (r>=n) r=n-1;
            st[i][j] = max(st[i-1][j],st[i-1][r]);
        }
    }
}

int query_small(int st[MAX_MM][MAX_NN],int l,int r) {
    int dis=(r-l+1);
    return max(st[lg2[dis]][l],st[lg2[dis]][r-pow2[lg2[dis]]+1]);
}

void make_st_big(int n,int *a,int st[MAX_MMM][MAX_NNN]) {
    for (int i=0;n>i;i++) {
        st[0][i] = a[i];
    }
    for (int i=1;MAX_MMM>i;i++) {
        for (int j=0;n>j;j++) {
            int r=j+pow2[i-1];
            if (r>=n) r=n-1;
            st[i][j] = max(st[i-1][j],st[i-1][r]);
        }
    }
}

int query_big(int st[MAX_MMM][MAX_NNN],int l,int r) {
    int dis=(r-l+1);
    return max(st[lg2[dis]][l],st[lg2[dis]][r-pow2[lg2[dis]]+1]);
}

void init() {
    pow2[0]=1;
    for (int i=1;MAX_P>i;i++) {
        pow2[i] = pow2[i-1]*2;
    }
    lg2[1] = 0;
    int id=1;
    for (int i=2;MAX_N>i;i++) {
        if (i==pow2[id]) {
            lg2[i] = lg2[i-1]+1;
            id++;
        }
        else {
            lg2[i] = lg2[i-1];
        }
    }
}

int tmp[MAX_NN];
int aa[MAX_NNN];

int main () {
    init();
    int n;
    scanf("%d",&n);
    for (int i=0;n>i;i++) {
        scanf("%d",&a[i]);
    }
    int small_sz=lg2[n];
    if (small_sz<2) small_sz=2;
    int totgroup=n/small_sz + !(n%small_sz==0);
    for (int i=0;totgroup>i;i++) {
        int t=0;
        for (int j=i*small_sz;i*small_sz+small_sz>j;j++) {
            if (j<n) tmp[t++] = a[j];
            else tmp[t++] = _INF;
        }
        make_st_small(small_sz,tmp,st_small[i]);
        aa[i] = query_small(st_small[i],0,small_sz-1);
    }
    make_st_big(totgroup,aa,st_big);
    int q;
    scanf("%d",&q);
    while (q--) {
        int l,r;
        scanf("%d %d",&l,&r);
        if (l>r) swap(l,r);
        l--;
        r--;
        if (l/small_sz == r/small_sz) {
            //in the same group
            int group_first=(l/small_sz)*small_sz;
            printf("%d\n",query_small(st_small[l/small_sz],l-group_first,r-group_first));
            continue;
        }
        int ans=max(query_small(st_small[l/small_sz],l-(l/small_sz)*small_sz,small_sz-1),
        query_small(st_small[r/small_sz],0,r-(r/small_sz)*small_sz));
        if (l/small_sz +1 == r/small_sz) printf("%d\n",ans);
        else printf("%d\n",max(ans,query_big(st_big,l/small_sz+1,r/small_sz-1)));
    }
}


(Zerojudge) d779: NOIP2009 3.最优贸易

https://zerojudge.tw/ShowProblem?problemid=d779

我是先想50%,之後才想到100%的!

在寫50%的時候,我發現圖是一個DAG,用DAG來維護一些東西。

那在推廣到100%的時候,我就在想,要怎麼把整張圖推廣到DAG --> SCC!

50%的版本:

#include <iostream>
#include <stdio.h>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;

const int MAX_N = 1e5 +6;

vector<int> edg[MAX_N];
int a[MAX_N];
int mn[MAX_N];
int mxans[MAX_N];
int deg[MAX_N];

int main () {
    int n,m;
    scanf("%d %d",&n,&m);
    for (int i=1;n>=i;i++) {
        scanf("%d",&a[i]);
        mn[i] = a[i];
        mxans[i]=0;
    }
    for (int i=0;m>i;i++) {
        int a,b,c;
        scanf("%d %d %d",&a,&b,&c);
        if (c==1) edg[a].push_back(b);
        else {
            edg[a].push_back(b);
            edg[b].push_back(a);
        }
        deg[b]++;
    }
    int ans=0;
    queue<int> que;
    que.push(1);
    while (!que.empty()) {
        int t=que.front();
        que.pop();
        mxans[t] = max(mxans[t],a[t]-mn[t]);
        for (int i:edg[t]) {
            mn[i] = min(mn[i],mn[t]);
            deg[i]--;
            mxans[i] = max(mxans[i],mxans[t]);
            if (deg[i]==0) que.push(i);
        }
    }
    printf("%d\n",mxans[n]);
}

100%的版本:

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;

const int MAX_N = 1e5 + 6;
const int INF = 1e9 + 7;

vector<int> edg[MAX_N];
vector<int> rev_edg[MAX_N];
vector<int> scc_edg[MAX_N];
int scc_mx[MAX_N],scc_mn[MAX_N],scc_ans[MAX_N];
int a[MAX_N];
bool visit[MAX_N];
int get_stamp[MAX_N];
int in_scc[MAX_N];
int ansmx[MAX_N];
int deg[MAX_N];

void gao1(int id,int &stamp) {
    visit[id]=1;
    for (int i:rev_edg[id]) {
        if (!visit[i]) {
            gao1(i,stamp);
        }
    }
    get_stamp[stamp++]=id;
}

void gao2(int id,int scc) {
    visit[id]=1;
    for (int i:edg[id]) {
        if (!visit[i]) {
            gao2(i,scc);
        }
    }
    in_scc[id]=scc;
    scc_mx[scc] = max(scc_mx[scc],a[id]);
    scc_mn[scc] = min(scc_mn[scc],a[id]);
}

int build_scc(int n) {
    memset(visit,0,sizeof(visit));
    int stamp=1;
    for (int i=1;n>=i;i++) {
        if (!visit[i]) {
            gao1(i,stamp);
        }
    }
    fill(scc_mx,scc_mx+MAX_N,-INF);
    fill(scc_mn,scc_mn+MAX_N,INF);
    memset(visit,0,sizeof(visit));
    int scc=0;
    for (int i=n;i>=1;i--) {
        if (!visit[get_stamp[i]]) {
            gao2(get_stamp[i],++scc);
        }
    }
    for (int i=1;n>=i;i++) {
        for (int j:edg[i]) {
            if (in_scc[i] != in_scc[j]) {
//                cout<<"build edge i = "<<in_scc[i]<<" , j = "<<in_scc[j]<<endl;
                scc_edg[in_scc[i]].push_back(in_scc[j]);
            }
        }
    }
    return scc;
}

int main () {
    int n,m;
    scanf("%d %d",&n,&m);
    for (int i=1;n>=i;i++) {
        scanf("%d",&a[i]);
    }
    for (int i=1;m>=i;i++) {
        int a,b,c;
        scanf("%d %d %d",&a,&b,&c);
        edg[a].push_back(b);
        rev_edg[b].push_back(a);
        if (c==2) {
            edg[b].push_back(a);
            rev_edg[a].push_back(b);
        }
    }
    int scc=build_scc(n);
//    cout<<"scc = "<<scc<<endl;
//    for (int i=1;n>=i;i++) {
//        cout<<"in_scc["<<i<<"] = "<<in_scc[i]<<endl;
//    }
    queue<int> que;
    for (int i=1;scc>=i;i++) {
        for (int j:scc_edg[i]) {
            deg[j]++;
        }
    }
    que.push(in_scc[1]);
    while (!que.empty()) {
        int t=que.front();
//        cout<<"T = "<<t<<endl;
        que.pop();
        ansmx[t] = max(ansmx[t],scc_mx[t]-scc_mn[t]);
        for (int i:scc_edg[t]) {
            deg[i]--;
            scc_mn[i] = min(scc_mn[i],scc_mn[t]);
            ansmx[i] = max(ansmx[i],ansmx[t]);
            if (deg[i] == 0) que.push(i);
        }
    }
    printf("%d\n",ansmx[in_scc[n]]);
}



2016年11月11日 星期五

(Zj) b903: 高中組-第九題:超級電池

http://zerojudge.tw/ShowProblem?problemid=b903

我首殺耶,開心XD



74% 解法:dp[i][j]代表選前i個超級石和前j個鑰石所得最大的能力值是多少,然後用類似LCS的更新手法,O(n^2)

100% 解法:先把K個配對按照 "超級石" 的大小排序,之後開一個dp陣列,dp[i]代表說,如果最後用了第i個 "鑰石" , 所能獲得最大的點數是多少。那更新dp陣列的轉移方程就是(假設現在超級石編號為a,鑰石編號為b,能力值為c)dp[b] = max(dp[1~b-1] + c,dp[b]),那,我們可以用segment tree來維護dp陣列,這樣複雜度就可以壓到O(n lg n)


#include <iostream>
#include <stdio.h>
#include <utility>
#include <algorithm>
using namespace std;

typedef long long LL;
typedef pair<LL,LL> pii;
typedef pair<pii,LL> piii;
const int MAX_N = 3e5 + 6;
const LL INF = 1e17+6;

struct Node {
    Node* lc;
    Node* rc;
    LL val;
    Node() {
        lc=rc=NULL;
        val = 0;
    }
};

LL pull(LL lc,LL rc) {
    return max(lc,rc);
}

Node* Build(int L,int R) {
    Node* node =new Node();
    if (L==R) {
        return node;
    }
    int mid=(L+R)>>1;
    node->lc=Build(L,mid);
    node->rc=Build(mid+1,R);
    return node;
}

void modify(Node* node,int L,int R,int pos,LL val) {
    if (L==R) {
        node->val = val;
        return;
    }
    int mid=(L+R)>>1;
    if (pos<=mid) modify(node->lc,L,mid,pos,val);
    else modify(node->rc,mid+1,R,pos,val);
    node->val = pull(node->lc->val,node->rc->val);
    return;
}

LL query(Node* node,int L,int R,int l,int r) {
    if (L>r || l>R) return 0;
    else if (l<=L && R<=r) return node->val;
    int mid=(L+R)>>1;
    return pull(query(node->lc,L,mid,l,r),query(node->rc,mid+1,R,l,r));
}

piii ipt[MAX_N];

int main () {
    int n,m,k;
    while (scanf("%d %d %d",&n,&m,&k) != EOF) {
        for (int x=1;k>=x;x++) {
            int i,j,k;
            scanf("%d %d %d",&i,&j,&k);
            ipt[x] = make_pair(make_pair(i,-j),k);
        }
        sort(ipt+1,ipt+k+1);
        Node* root = Build(1,m);
        for (int x=1;k>=x;x++) {
            int i=ipt[x].first.first,j=-ipt[x].first.second,k=ipt[x].second;
            LL t=query(root,1,m,1,j-1);
            modify(root,1,m,j,max(query(root,1,m,j,j),t+k));
        }
        printf("%lld\n",root->val);
    }
}


2016年11月7日 星期一

(Zj) b692: 第五題:棕櫚儀式

http://zerojudge.tw/ShowProblem?problemid=b692

仔細想想後,可以發現說,這題可以簡化成:有n個數字,你可以掛正負號,但是至少有一個正的 & 一個負的,然後最後使 sum 最大。


#include <iostream>
#include <algorithm>
#include <stdio.h>
using namespace std;

typedef long long LL;
const int MAX_N = 1e6 + 6;

LL a[MAX_N];

int main () {
    int n;
    while (scanf("%d",&n) != EOF) {
        for (int x=1;n>=x;x++) {
            scanf("%lld",&a[x]);
        }
        sort(a+1,a+n+1);
        if (n==1) printf("%lld\n",a[1]);
        else {
            long long ans = 0;
            ans = a[n] - a[1];
            for (int x=2;n-1>=x;x++) {
                if (a[x] > 0) ans += a[x];
                else ans -= a[x];
            }
            printf("%lld\n",ans);
        }
    }
}

2016年10月20日 星期四

(Zj) a200. APIO2010 1.特别行动队 [dp斜率優化]


http://zerojudge.tw/ShowProblem?problemid=a200

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <queue>
using namespace std;

typedef long long LL;
const int MAX_N = 1e6 + 6;

LL p[MAX_N];
LL dp[MAX_N];
int v[MAX_N];

LL a,b,c;

struct line {
    LL m,b;
    LL val(LL x) {
        return m*x+b;
    }
};

deque<line> dq;

LL t(int i) {
    return a*p[i]*p[i] + b*p[i] + c;
}

LL f(int i) {
    return a*p[i]*p[i] - b*p[i] + dp[i];
}

LL g(int i) {
    return p[i];
}

LL h(int i) {
    return -2 * a * p[i];
}

bool check(line x,line y,line z) {
    return (x.b-z.b)*(y.m-x.m)<=(z.m-x.m)*(x.b-y.b);
}

int main () {
    int n;
    while (scanf("%d",&n) != EOF) {
        scanf("%lld %lld %lld",&a,&b,&c);
        p[0]=0;
        for (int x=1;n>=x;x++) {
            scanf("%d",&v[x]);
            p[x]=p[x-1]+v[x];
        }
        dp[0] = t(0);
        dq.clear();
        dq.push_back((line){0,0});
        for (int i=1;n>=i;i++) {
            while (dq.size() >= 2 && dq[0].val(g(i)) < dq[1].val(g(i))) dq.pop_front();
            dp[i] = dq[0].val(g(i)) + t(i);
            line newline{h(i),f(i)};
            while (dq.size() >= 2 && check(dq[dq.size()-2],dq[dq.size()-1],newline)) {
                dq.pop_back();
            }
            dq.push_back(newline);
        }
        printf("%lld\n",dp[n]);
    }
}

相似題:http://acm.hdu.edu.cn/showproblem.php?pid=3507





2016年7月9日 星期六

a066: HNOI2002 营业额统计

http://cat.nknush.kh.edu.tw/ShowProblem?problemid=a066

如果題目少輸入的話,用0去算!!!(害我debug了大約20min!!!)

很重要!!!

我放的code是如果測資是okay的code,直接貼上去是會吃NA(score : 80)的喔!

想法:開一個treap維護數字XDDD,數值線段樹也應該可以,不過最近發現treap比較好co ^_^


#include <iostream>
#include <stdio.h>
#include <ctime>
#include <cstdlib>
#include <cmath>
using namespace std;

struct Treap {
 Treap* lc;
 Treap* rc;
 int pri;
 int key;
 int mn,mx;
 Treap(int _key) {
  lc=rc=NULL;
  mn=mx=key=_key;
  pri=rand();
 }
};

const int INF = 1e9+7;

int Mx(Treap* t) {
 return t?t->mx:-INF;
}

int Mn(Treap* t) {
 return t?t->mn:INF;
}

void pull(Treap* t) {
 t->mx = max(Mx(t->lc),Mx(t->rc));
 t->mx = max(t->mx,t->key);
 t->mn = min(Mn(t->lc),Mn(t->rc));
 t->mn = min(t->mn,t->key);
}

Treap* merge(Treap* a,Treap* b) {
 if (!a || !b) return a?a:b;
 else if (a->pri > b->pri) {
  a->rc = merge(a->rc,b);
  pull(a);
  return a;
 }
 else {
  b->lc = merge(a,b->lc);
  pull(b);
  return b;
 }
}

void split(Treap* t,int k,Treap* &a,Treap* &b) {
 if (!t) a=b=NULL;
 else if (t->key <= k) {
  a=t;
  split(t->rc,k,a->rc,b);
  pull(a);
 }
 else {
  b=t;
  split(t->lc,k,a,b->lc);
  pull(b);
 }
}

int main () {
 srand(time(NULL));
 int n;
 while (scanf("%d",&n) != EOF) {
  Treap* root=NULL;
  long long int sum=0;
  for (int x=0;n>x;x++) {
   int i;
   scanf("%d",&i);
   if (x==0) {
    sum+=i;
    root = merge(root,new Treap(i));
   }
   else {
    Treap* tl;
    Treap* tr;
    split(root,i-1,tl,root);
    split(root,i,root,tr);
    if (tl) pull(tl);
    if (tr) pull(tr);
    int MX=Mx(tl);
    int MN=Mn(tr);
//    cout<<"mx = "<<MX<<" , mn = "<<MN<<endl;
    if (!root) sum+=min(abs(i-MX),abs(i-MN));
    root = merge(root,new Treap(i));
    root = merge(merge(tl,root),tr);
   }
  }
  printf("%lld\n",sum);
 }
}

2016年7月6日 星期三

a457: TOI2010 第五題:餐廳評鑑

http://zerojudge.tw/ShowProblem?problemid=a457

先按照第一個值(s)排序之後,尋找第二個值(r)的逆序數對個數(在不同第一個值之間)。

#include <iostream>
#include <stdio.h>
#include <utility>
#include <algorithm>
using namespace std;

const int MAX_N = 2e5 + 6;
typedef pair<int,int> pii;
vector<int> num;

struct Node {
 Node* lc;
 Node* rc;
 int val;
 Node() {
  lc=rc=NULL;
  val=0;
 }
 void pull() {
  val=lc->val + rc->val;
 }
};

Node* Build(int L,int R) {
 Node* node = new Node();
 if (L==R) {
  return node;
 }
 int mid=(L+R)>>1;
 node->lc=Build(L,mid);
 node->rc=Build(mid+1,R);
 node->pull();
 return node;
}

void modify(Node* node,int L,int R,int pos) {
 if (L==R) {
  node->val += 1;
  return;
 }
 int mid=(L+R)>>1;
 if (pos<=mid) modify(node->lc,L,mid,pos);
 else modify(node->rc,mid+1,R,pos);
 node->pull();
 return;
}

int query(Node* node,int L,int R,int l,int r) {
 if (L>r || l>R) return 0;
 else if (l<=L && R<=r) return node->val;
 int mid=(L+R)>>1;
 return query(node->lc,L,mid,l,r) + query(node->rc,mid+1,R,l,r);
}

pii s[MAX_N];

int main () {
 int k,m;
 while (scanf("%d %d",&k,&m) != EOF) {
  for (int x=0;k>x;x++) {
   int i;
   scanf("%d",&i);
   s[x].first=i;
  }
  for (int x=0;k>x;x++) {
   int i;
   scanf("%d",&i);
   s[x].second=i;
  }
  sort(s,s+k);
  for (int x=0;k>x;x++) {
   num.push_back(s[x].second);
  }
  sort(num.begin(),num.end());
  num.resize(unique(num.begin(),num.end()) - num.begin());
  for (int x=0;k>x;x++) {
   s[x].second = lower_bound(num.begin(),num.end(),s[x].second) - num.begin() + 1;
  }
  long long ans=0;
  int n=num.size();
  Node* root = Build(1,n);
  for (int x=0;k>x;x++) {
   vector<int> tmp;
   tmp.push_back(s[x].second);
   int val=s[x].first;
   while (k>x && s[x+1].first == val) {
    x++;
    tmp.push_back(s[x].second);
   }
   for (auto iter=tmp.begin();iter!=tmp.end();iter++) {
    int t=*iter;
    ans+=query(root,1,n,t+1,n);
   }
   for (auto iter=tmp.begin();iter!=tmp.end();iter++) {
    int t=*iter;
    modify(root,1,n,t);
   }
  }
  printf("%lld\n",ans);
 }
}

2016年7月5日 星期二

(Zj) a300: NOIP2011 Day1.2.选择客栈

http://zerojudge.tw/ShowProblem?problemid=a300

我的想法是說:

先觀察有一個很神奇的單調性:假設固定一個開始的客棧a , 如果a ~ b的選擇是okay的,那a~c (c>b) 也一定是okay的!!!(可以仔細想想為甚麼!)

藉由這個神奇的性質,我們可以開一棵線段樹維護最小值,然後binary search,就好了。

#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;

#define int long long

const int MAX_N = 2e5 + 6;
const int INF = 1e15+7;

vector<int> kind[MAX_N];

struct Node {
 Node* lc;
 Node* rc;
 int val;
 Node() {
  lc=rc=NULL;
  val=0;
 }
 void pull() {
  val=min(lc->val,rc->val);
 }
};

int v[MAX_N];
int kd[MAX_N]; //kind

Node* Build(int L,int R) {
 Node* node = new Node();
 if (L==R) {
  node->val=v[L];
  return node;
 }
 int mid=(L+R)>>1;
 node->lc=Build(L,mid);
 node->rc=Build(mid+1,R);
 node->pull();
 return node;
}

int query(Node* node,int L,int R,int l,int r) {
 if (l>R || L>r) return INF;
 else if (l<=L && R<=r) return node->val;
 int mid=(L+R)>>1;
 return min(query(node->lc,L,mid,l,r) , query(node->rc,mid+1,R,l,r));
}

main () {
 int n,k,p;
 while (scanf("%lld %lld %lld",&n,&k,&p) != EOF) {
  for (int x=1;n>=x;x++) {
   scanf("%lld %lld",&kd[x],&v[x]);
   kind[kd[x]].push_back(x);
  }
  Node* root=Build(1,n);
  long long ans=0;
  for (int i=0;k>i;i++) {
//   cout<<"i="<<i<<endl;
   if (kind[i].size()>1) {
    int len=kind[i].size();
    for (int j=0;len-1>j;j++) {
//     cout<<"j="<<j<<endl;
     int L=j,R=len-1;
     while (R-L!=1) {
//      cout<<"L ~ R : "<<L<<" ~ "<<R<<endl;
      int mid=(L+R)>>1;
      if (query(root,1,n,kind[i][j],kind[i][mid]) <= p) R=mid;
      else L=mid;
     }
//     cout<<"j="<<j<<" , L="<<L<<endl;
     if (query(root,1,n,kind[i][j],kind[i][R]) > p) break;
     else ans+=(len-R);
//     cout<<"ans = "<<ans<<endl;
    }
   }
//   cout<<"ans="<<ans<<endl;
  }
  printf("%lld\n",ans);
 }
}