1. 程式人生 > >【Codeforces Round #397】Codeforces 765F Souvenirs【解法一】

【Codeforces Round #397】Codeforces 765F Souvenirs【解法一】

Artsem is on vacation and wants to buy souvenirs for his two
teammates. There are n souvenir shops along the street. In i-th shop
Artsem can buy one souvenir for ai dollars, and he cannot buy more
than one souvenir in one shop. He doesn’t want to introduce envy in
his team, so he wants to buy two souvenirs with least possible
difference in price.

Artsem has visited the shopping street m times. For some strange
reason on the i-th day only shops with numbers from li to ri were
operating (weird? yes it is, but have you ever tried to come up with a
reasonable legend for a range query problem?). For each visit, Artsem
wants to know the minimum possible difference in prices of two
different souvenirs he can buy in the opened shops.

In other words, for each Artsem’s visit you should find the minimum
possible value of |as - at| where li ≤ s, t ≤ ri, s ≠ t. Input

The first line contains an integer n (2 ≤ n ≤ 105).

The second line contains n space-separated integers a1, …, an
(0 ≤ ai ≤ 109).

The third line contains the number of queries m (1 ≤ m ≤ 3·105).

Next m lines describe the queries. i-th of these lines contains two
space-separated integers li and ri denoting the range of shops working
on i-th day (1 ≤ li < ri ≤ n). Output

Print the answer to each query in a separate line.

解法二(線段樹套平衡樹)見這裡
以下對於i<j,只考慮ai>aj的情況。另外的情況只需要把原序列倒過來再做一遍。
把詢問離線,從左到右加入右端點,維護每個左端點的答案,就可以知道以當前點為右端點的所有詢問的答案。
當加入ai時,首先,原來有的答案還成立,但是可以新加入一些有ai參與的答案。首先找到最大的j<i使得ajai,顯然j+1往後的答案不會被修改。可以用ajai的值更新1..j的答案。然後再找到最大的j<j使得aiaj<aj,顯然j+1往後的答案不會被修改,可以用ajai的值更新1..j的答案。重複這樣的操作即可,可以用線段樹維護答案。
容易看出這樣的做法最多要操作O(n)次,但是可以發現只有滿足ajai<ajajj才是值得更新的,因為任何包含ji的區間一定包含jj,這樣的答案肯定會在反向時被考慮。於是aj的範圍縮小到了[ai,(ai+aj)/2)。換句話說,ajai每次至少縮小一半。這樣只用操作O(logai)次了。
但是如果每次暴力尋找j仍是O(n)的,不可取。可以離散化以後建立一棵權值線段樹維護最後出現的位置,每掃描過一個元素就加入線段樹,不難求出某一段權值的最後出現位置。當然也可以用平衡樹來維護。
最後的複雜度是O(nlognlogai+mlogn)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<vector>
using namespace std;
#define MP(a,b) make_pair(a,b)
const int oo=0x3f3f3f3f;
int a[100010],tag[400010],
late[400010],ord[100010],newa[100010],
ans[300010],ql[300010],qr[300010],
n,nn,m;
vector<pair<int,int> > query[300010];
int findmx(int p,int L,int R,int l,int r)
{
    if (l<=ord[L]&&ord[R]<r) return late[p];
    int mid=(L+R)/2,ret=-1;
    if (l<=ord[mid]) ret=max(ret,findmx(p*2,L,mid,l,r));
    if (ord[mid+1]<r) ret=max(ret,findmx(p*2+1,mid+1,R,l,r));
    return ret;
}
void add(int p,int L,int R,int k,int x)
{
    if (L==R)
    {
        late[p]=x;
        return;
    }
    int mid=(L+R)/2;
    if (k<=mid) add(p*2,L,mid,k,x);
    else add(p*2+1,mid+1,R,k,x);
    late[p]=max(late[p*2],late[p*2+1]);
}
void modi(int p,int L,int R,int l,int r,int x)
{
    if (l<=L&&R<=r)
    {
        tag[p]=min(tag[p],x);
        return;
    }
    int mid=(L+R)/2;
    if (l<=mid) modi(p*2,L,mid,l,r,x);
    if (r>=mid+1) modi(p*2+1,mid+1,R,l,r,x);
}
int qry(int p,int L,int R,int k)
{
    if (L==R) return tag[p];
    int mid=(L+R)/2;
    if (k<=mid)
    {
        tag[p*2]=min(tag[p*2],tag[p]);
        return qry(p*2,L,mid,k);
    }
    else
    {
        tag[p*2+1]=min(tag[p*2+1],tag[p]);
        return qry(p*2+1,mid+1,R,k);
    }
}
void solve()
{
    int x,y,p;
    memset(tag,0x3f,sizeof(tag));
    memset(late,0xff,sizeof(late));
    for (int i=1;i<=n;i++)
    {
        x=a[i];
        y=2*oo-x;
        while (x<y&&(p=findmx(1,1,nn,x,(x+y+1)/2))!=-1)
        {
            modi(1,1,n,1,p,a[p]-a[i]);
            y=a[p];
        }
        for (int j=0;j<query[i].size();j++)
            ans[query[i][j].second]=min(ans[query[i][j].second],qry(1,1,n,query[i][j].first));
        add(1,1,nn,newa[i],i);
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]),ord[i]=a[i];
    sort(ord+1,ord+n+1);
    nn=unique(ord+1,ord+n+1)-ord-1;
    for (int i=1;i<=n;i++)
        newa[i]=lower_bound(ord+1,ord+nn+1,a[i])-ord;
    scanf("%d",&m);
    for (int i=1;i<=m;i++) scanf("%d%d",&ql[i],&qr[i]);
    for (int i=1;i<=m;i++) query[qr[i]].push_back(MP(ql[i],i));
    memset(ans,0x3f,sizeof(ans));
    solve();
    for (int i=1;i*2<=n;i++) swap(a[i],a[n-i+1]),swap(newa[i],newa[n-i+1]);
    memset(query,0,sizeof(query));
    for (int i=1;i<=m;i++) query[n-ql[i]+1].push_back(MP(n-qr[i]+1,i));
    solve();
    //for (int i=1;i<=m;i++) if (ans[i]==oo) printf("no:%d\n",i);
    for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
}

相關推薦

Codeforces Round #397Codeforces 765F Souvenirs解法

Artsem is on vacation and wants to buy souvenirs for his two teammates. There are n souvenir shops along the street. In i-th

Educational Codeforces Round 55 (Rated for Div. 2) E. Increasing Frequency滾動陣列優化暴力

E. Increasing Frequency 題意 給你一個數列,你可以選擇在[l,r]區間同時加或者減一個值, 在一次操作後,這個序列最多有多少個值等於c 做法 首先我們要想明白的是,a[l]一定是等於a[r]的 如果a[l]!=a[r],那麼我們肯定可以縮小這個區間,

Codeforces Round #515 (Div. 3)B. Heaters模擬,思維

B. Heaters time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output Vova's house

Codeforces Round #508 (Div. 2) D. Slime思維

D. Slime time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output There are 

Codeforces Round #248 (Div. 2) B題 資料結構:樹狀陣列

題目大意:給n(1 ≤ n ≤ 105)個數據(1 ≤ vi ≤ 109),其中有m(1 ≤ m ≤ 105)個問題,分兩種,第一種:給出l,r,讓你求出v[l],v[r]之間的所有資料和;第二種:

Codeforces Round #338 (Div. 2)D. Multipliers費馬小定理+組合數學

D. Multipliers time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output

Codeforces Round #397 F Souvenirs(線段樹)

考慮離線,我們從左往右掃,對於現在的點i,我們先找到離i最近的j使得a[j]>=a[i],然後我們可以知道詢問左區間在1~j的詢問的答案小於等於a[j]-a[i],我們可以用線段樹更新答案,然後還可能存在f<j使得a[f]>=a[i],a[f]-a[i]&

Codeforces Round #397 (Div. 1 + Div. 2 combined) F.Souvenir

洛谷傳送門 題目描述 Artsem is on vacation and wants to buy souvenirs for his two teammates. There are nnn souvenir shops along the street.

Educational Codeforces Round 17 C && codeforces 762C C. Two strings 詳細的題解 (前後綴應用)

C. Two strings time limit per test 2 seconds memory limit per test 256 megabytes input stan

SDOI2016bzoj4516 生成魔咒解法

每次在SAM中加入一個字元,維護每個點的vali−valfaili即可。 因為字符集很大,離散化以後也開不下,可以用map存轉移。 #include<cstdio> #include&l

推導Codeforces Round #411 (Div. 1) A. Find Amir

div sca ace space for amp clu ret blog 1 2 3 4 5 6 7 4-5-3-6-2-7-1 答案是(n-1)/2 #include<cstdio> using namespace std; int n; int mai

貪心multisetTinkoff Challenge - Final Round (Codeforces Round #414, rated, Div. 1 + Div. 2) C. Naming Company

nal head 1+n final ase algorithm erase 集合 clu 考慮兩個人,先把各自的集合排個序,丟掉一半,因為比較劣的那一半一定用不到。 然後貪心地放,只有兩種決策,要麽把一個最優的放在開頭,要麽把一個最劣的放在結尾。 如果我的最優的比對方所有

動態規劃 Codeforces Round #416 (Div. 2) C. Vladik and Memorable Trip

and main spa def esp 動態 return 價值 can 劃分那個序列,沒必要完全覆蓋原序列。對於劃分出來的每個序列,對於某個值v,要麽全都在該序列,要麽全都不在該序列。 一個序列的價值是所有不同的值的異或和。整個的價值是所有劃分出來的序列的價值之和。

分類討論spfaBFSCodeforces Round #416 (Div. 2) D. Vladik and Favorite Game

邊界情況 code def bfs spa eof scan string amp 那個人第一步肯定要麽能向下走,要麽能向右走。於是一定可以判斷出上下是否對調,或者左右是否對調。 然後他往這個方向再走一走就能發現一定可以再往旁邊走,此時就可以判斷出另一個方向是否對調。 都判

動態規劃Codeforces Round #406 (Div. 2) C.Berzerk

[1] space node sca 一個 for 隊列 ber 動態規劃 有向圖博弈問題。 能轉移到一個必敗態的就是必勝態。 能轉移到的全是必勝態的就是必敗態。 轉移的時候可以用隊列維護。 可以看這個 http://www.cnblogs.com/quintessence

貪心 Codeforces Round #419 (Div. 1) A. Karen and Game

blog true 刪除 round 貪心 cnblogs pac names namespace 容易發現,刪除的順序不影響答案。 所以可以隨便刪。 如果行數大於列數,就先刪列;否則先刪行。 #include<cstdio> #include<algo

找規律遞推二項式定理Codeforces Round #419 (Div. 1) B. Karen and Test

main turn logs pow 分享 string ren () 奇數 打個表出來看看,其實很明顯。 推薦打這倆組 11 1 10 100 1000 10000 100000 1000000 10000000 100000000 1000000000 10000000

推導Codeforces Round #364 (Div. 2) D. As Fast As Possible

std %d while pre 分享 mage oss 時間 http 一種方法是二分總時間,復雜度O(nlogn)。 另外我們可以證明,當所有人同時到達終點的時候,是最優的,因為沒有人的時間“浪費”了。 我們又發現,每個人的運動過程總是兩段,要麽是走路,要麽是坐車。於