DAY 4模擬賽

DAY 4

zhx出題


 

T1

裂變連接

【問題描述】

你是能看到第一題的 friends 呢。git

——hja數組

衆所周知,小蔥同窗擅長計算,尤爲擅長計算組合數,但這個題和組合數沒什麼關係。優化

如今有𝑁個異構體在前線排成一排參與戰鬥,但因爲地方火力過於兇猛,異構體們都受到了不一樣程度的損傷。爲了可以讓全部異構體繼續戰鬥,避免因爲能量不均衡致使的爆炸,異構體們須要使得他們彼此之間的能量值同樣。記第𝑖個異構體當前𝑒𝑖的能量,每一個時刻,每一個異構體均可以作以下三種操做中的一種:ui

一、傳遞1的能量給本身左邊相鄰的異構體(若是存在)。spa

二、傳遞1的能量給本身右邊相鄰的異構體(若是存在)。code

三、傳遞1的能量給本身(摸魚)。blog

爲了儘快的回到前線做戰,異構體們但願在最短的時間內使得全部異構體的能量值同樣,問最短期。數據保證有解。操做過程當中本身的能量能夠變爲負數。排序

【輸入格式】

第一行一個整數𝑁表明異構體數量。遊戲

接下來一行𝑁個整數表明能量值。ci

【輸出格式】

輸出一行一個數表明答案。

【樣例輸入】

3

1 0 5

【樣例輸出】

3

【數據規模與約定】

對於30%的數據,𝑁 ≤ 10, |𝑒𝑖| ≤ 10。

對於60%的數據,𝑁 ≤ 100。

對於另外20%的數據,能量值的序列必定先單調遞增再單調遞減。

對於100%的數據,1 ≤ 𝑁 ≤ 10^5, |𝑒𝑖| ≤ 10^5。

 

題解

肯定一個異構體在最優狀況下傳遞的能量

 

 

每個都達到v

前綴和表示總能量

由於都要變成v,因此前i-1總能量應該是(i-1)*v

也就是說i不須要向左邊輸出能量

右邊最後的能量值應該是(n-i)*v

節點的做用就是把左邊的能量的差值所有轉移到右邊去,這個差值就是它傳遞的能量

枚舉每個節點

總共四種狀況

1.左邊多,右邊少 

2.左邊少,右邊多  

3.左邊多,右邊多,說明i本身少    此時須要兩邊與指望能量差值的max

4.左邊少,右邊少,說明i本身多    此時須要兩邊與指望能量差值的和

而後取max就好了

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

using namespace std;

const int maxn=100010;

int n,z[maxn];

long long sum[maxn];

void read()
{
    scanf("%d",&n);
    for (int a=1;a<=n;a++)
        scanf("%d",&z[a]);
}

long long work()
{
    for (int a=1;a<=n;a++)
        sum[a] = sum[a-1]+z[a];
    if (sum[n]%n) return -1;
    long long ans=0,v=sum[n]/n;
    for (int a=1;a<=n;a++)
    {
        long long l=sum[a-1]-1ll*(a-1)*v;
        long long r=sum[n]-sum[a]-1ll*(n-a)*v;
        if (l<0 && r<0) ans=max(ans,-l-r);
        ans=max(ans,max(abs(l),abs(r)));
    }
    return ans;
}

int main()
{
    read();
    printf("%lld\n",work());

    return 0;
}

T2

死亡鴿者

【問題描述】

你是能看到第二題的 friends 呢。

——aoao

衆所周知,小蔥同窗擅長計算,尤爲擅長計算組合數,但這個題和組合數沒什麼關係。

死亡鴿者最喜歡的事情就是咕咕咕,他的座右銘「風蕭蕭兮易水寒,壯士一去兮不復返,然鴿子至今未到」也天天被他所忘記。今天,死亡鴿者又要開始咕咕的旅行了。如今有𝑁座城市排成一排,死亡鴿者會從第一座城市一直走到最後一座城市。每一個城市都有一個數𝑎𝑖,每次死亡鴿者能夠選擇取走或者不取走這個數,但想要取走這個數的話要求這個數必須是全部已經取走的數的倍數或者約數。如今問死亡鴿者從第一座城市走到最後一座城市的過程當中,最多取走多少個數。

【輸入格式】

第一行一個數𝑁。

接下來一行𝑁個數表明每一個城市的數。

【輸出格式】

一行一個數表明答案。

【樣例輸入】

3

2 6 3

【樣例輸出】

2

【數據規模與約定】

對於20%的數據,𝑁 ≤ 10。

對於50%的數據,𝑁 ≤ 1000。

對於另外20%的數據,全部城市的數不重複。

對於100%的數據,1 ≤ 𝑁, 𝑎𝑖 ≤ 10^6。

 

題解

從左向右走?其實沒啥用,答案和這個沒有關係

能夠把這些數從小到大排序,這樣就只用考慮這個數是否是前邊的數的倍數就好了

最長上升子序列

F[i]表示選擇a[i]的狀況下序列長度最長是多少

轉移就是枚舉它前面的數

只須要保證a[i]是a[j]的倍數就好了,由於a[j]必定已是前邊的數的倍數了

f[i]=max(f[j])+1  a[i]|a[j]

50pts

 

若是沒有重複的

答案的長度不超過logn

那麼就能夠去重,由於只要一個重複的選上,其餘就必須選

去重以後跑搜索也能夠過

或者把dp在去重以後的數組上作,也能夠過

100pts

 

正解?

全部的數<=10^6這個條件尚未用過

能夠開10^6個桶記錄每一個數出現了多少次

用f[i]表示如今取出的序列在最後一個數爲i的狀況下最長有多長

如今取出的數是i,那麼以後的都是i的倍數

f[k*i]=max(f[i]+cnt[k*i])

                                              

 

 

 

咱們並不關心每一個數是多少,只關心有多少個

#include<cstdio>
#include<cstdlib>
#include<cstring>

using namespace std;

const int maxn=1000010;

int n,cnt[maxn],f[maxn];

int main()
{
    scanf("%d",&n);
    for (int a=1;a<=n;a++)
    {
        int v;
        scanf("%d",&v);
        cnt[v]++;
        f[v]++;
    }
    int ans=0;
    for (int a=1;a<=1000000;a++)
        if (f[a])
        {
            if (f[a]>ans) ans=f[a];
            for (int b=a+a;b<=1000000;b+=a)
                if (cnt[b] && f[a]+cnt[b]>f[b]) f[b]=f[a]+cnt[b];
        }
    printf("%d\n",ans);


    return 0;
}

T3

進階之災

【問題描述】

你是能看到第三題的 friends 呢。

——laekov

衆所周知,小蔥同窗擅長計算,尤爲擅長計算組合數,但這個題和組合數沒什麼關係。

TarjanLusa 是一款風靡全球的卡牌遊戲,在這款遊戲中,你須要一層一層的前進,擊敗像萌死戳、天啓騎士等 boss,最終來到心臟面前一決勝負。爲了可以有一套更好的卡牌來面對心臟,咱們須要在每一層選擇更好的卡牌。假設咱們總共有𝑁層,在第𝑖層的時候,咱們能夠從兩張卡牌中選擇一張加入咱們的卡組,這兩張卡牌的戰鬥力分別爲𝑎𝑖, 𝑏𝑖。在通過𝑁層的選擇以後,咱們便會有一套𝑁張卡的卡組,而整套卡組的戰鬥力取決於卡牌與卡牌之間戰鬥力差值的絕對值的最小值。可是心臟是一個很是強大的敵人,若是咱們不能擁有強大的戰鬥力,人類就會一敗塗地。因此,如今咱們想知道,戰鬥力最大多是多少。

【輸入格式】

第一行一個整數𝑁。

接下來𝑁行每行兩個整數𝑎𝑖, 𝑏𝑖。

【輸出格式】

輸出一行一個數表明答案。

【樣例輸入】

3

1 2

3 4

5 6

【樣例輸出】

2

【數據規模與約定】

對於20%的數據,1 ≤ 𝑁 ≤ 10。

對於50%的數據,1 ≤ 𝑁 ≤ 100。

對於另外20%的數據,全部卡牌戰鬥力的最大值減去最小值小於等於100。

對於100%的數據,1 ≤ 𝑁 ≤ 10^5, 1 ≤ 𝑎𝑖, 𝑏𝑖 ≤ 10^9

 

題解

2-SAT?

然鵝不會寫

最小值最大化

二分

看看可否構造出戰鬥力大於等於v的卡牌組

如今有a1,b1和a2,b2

若是|a1-a2|,那麼a1,a2不能同時選,從a1向b2連邊

同理,從a2向b2連邊

直接n^2枚舉卡牌,而後跑2-SAT

50pts

 

不能過100pts的緣由

緣由:枚舉建邊是n^2的

 

60~70 pts 直接輸出0  ??? 抽屜原理...我吐了

 

發現邊太多了,咱們想盡可能節省邊的數量

線段樹優化建圖(大霧)

把a1b1,a2b2...anbn拿出來從小到大排序

c1,c2,...,c2n

考慮ci,必定對應原來的某個數

兩條邊之間建邊的條件是|ai-aj|<v

也就是ci前面一段和ci後面一段,是連續的區間

李姐爲是區間加邊的操做

線段樹

若是第五個節點排好序以後是a2  若是有一個衝突的,必定和b2連邊

那麼在a2這個節點下面掛一個b2

區間加邊

假如a3想對2-6之間的節點加邊

2-6拆成[2,2],[3,4],[5,6]

從a3對這三個區間加邊

                                                         

 

 

 

連通性沒有發生改變

圖和原來的是等價的

Zhxtql

因此邊數是n log^2 n

而後跑tarjan

 

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<algorithm>

using namespace std;

const int BUF_SIZE = 30;
char buf[BUF_SIZE], *buf_s = buf, *buf_t = buf + 1;
  
#define PTR_NEXT() \
    { \
        buf_s ++; \
        if (buf_s == buf_t) \
        { \
            buf_s = buf; \
            buf_t = buf + fread(buf, 1, BUF_SIZE, stdin); \
        } \
    }
   
#define readint(_n_) \
    { \
        while (*buf_s != '-' && !isdigit(*buf_s)) \
            PTR_NEXT(); \
        bool register _nega_ = false; \
        if (*buf_s == '-') \
        { \
            _nega_ = true; \
            PTR_NEXT(); \
        } \
        int register _x_ = 0; \
        while (isdigit(*buf_s)) \
        { \
            _x_ = _x_ * 10 + *buf_s - '0'; \
            PTR_NEXT(); \
        } \
        if (_nega_) \
            _x_ = -_x_; \
        (_n_) = (_x_); \
    }

#define readstr(_s_) \
    { \
        while (!isupper(*buf_s)) \
            PTR_NEXT(); \
        char register *_ptr_ = (_s_); \
        while (isupper(*buf_s) || *buf_s == '-') \
        { \
            *(_ptr_ ++) = *buf_s; \
            PTR_NEXT(); \
        } \
        (*_ptr_) = '\0'; \
    }

#define readlonglong(_n_) \
    { \
        while (*buf_s != '-' && !isdigit(*buf_s)) \
            PTR_NEXT(); \
        bool register _nega_ = false; \
        if (*buf_s == '-') \
        { \
            _nega_ = true; \
            PTR_NEXT(); \
        } \
        long long register _x_ = 0; \
        while (isdigit(*buf_s)) \
        { \
            _x_ = _x_ * 10 + *buf_s - '0'; \
            PTR_NEXT(); \
        } \
        if (_nega_) \
            _x_ = -_x_; \
        (_n_) = (_x_); \
    }

#define wmt 1,(n<<1),1
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

const int maxn=100010;
const int maxp=maxn+(maxn<<2);
const int maxm=maxn+maxp+maxn*20;

int n,size,cnt,en,t,dfn[maxp],low[maxp],s[maxp],belong[maxp],pos[maxn];

bool instack[maxp];

struct edge
{
    int e;
    edge *next;
}*v[maxp],ed[maxm];

void add_edge(int s,int e)
{
    en++;
    ed[en].next=v[s];v[s]=ed+en;v[s]->e=e;
}

struct rec
{
    int v,p;
    rec(){}
    rec(int a,int b)
    {
        v=a;p=b;
    }
}z[maxn];

bool operator<(const rec &a,const rec &b)
{
    return a.v<b.v;
}

void dfs(int p)
{
    t++;
    dfn[p]=low[p]=t;
    instack[p]=true;
    s[++size]=p;
    for (edge *e=v[p];e;e=e->next)
        if (!dfn[e->e])
        {
            dfs(e->e);
            low[p]=min(low[p],low[e->e]);
        }
        else
        {
            if (instack[e->e]) low[p]=min(low[p],dfn[e->e]);
        }
    if (dfn[p]==low[p])
    {
        cnt++;
        while (s[size]!=p)
        {
            belong[s[size]]=cnt;
            instack[s[size]]=false;
            size--;
        }
        belong[p]=cnt;
        instack[p]=false;
        size--;
    }
}

void build(int l,int r,int rt)
{
    if (l==r)
    {
        add_edge(rt+(n<<1),z[l].p<=n?z[l].p+n:z[l].p-n);
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    add_edge(rt+(n<<1),(rt<<1)+(n<<1));
    add_edge(rt+(n<<1),(rt<<1|1)+(n<<1));
}

void insert(int l,int r,int rt,int nowl,int nowr,int p)
{
    if (nowl<=l && r<=nowr)
    {
        add_edge(p,rt+(n<<1));
        return;
    }
    int m=(l+r)>>1;
    if (nowl<=m) insert(lson,nowl,nowr,p);
    if (m<nowr) insert(rson,nowl,nowr,p);
}

bool check(int k)
{
    en=0;cnt=0;
    memset(v,0,sizeof(v));
    memset(dfn,0,sizeof(dfn));

    build(wmt);

    int r=1,l=1;

    for (int a=1;a<=(n<<1);a++)
    {
        int op,p=z[a].p;
        if (p<=n) op=pos[p+n];
        else op=pos[p-n];
        while (r<=a && z[r].v <= z[a].v-k)
            r++;
        if (r<a && r>=1 && z[r].v > z[a].v-k) 
        {
            if (op>=r && op<=a-1)
            {
                if (op>r) insert(wmt,r,op-1,z[a].p);
                if (op<a-1) insert(wmt,op+1,a-1,z[a].p);
            }
            else insert(wmt,r,a-1,z[a].p);
        }
        while (l<=(n<<1) && z[l].v < z[a].v+k)
            l++;
        l--;
        if (l>a && l<=(n<<1) && z[l].v < z[a].v+k) 
        {
            if (op>=a+1 && op<=l)
            {
                if (op>a+1) insert(wmt,a+1,op-1,z[a].p);
                if (op<l) insert(wmt,op+1,l,z[a].p);
            }
            else insert(wmt,a+1,l,z[a].p);
        }
    }

    for (int a=1;a<=(n<<1);a++)
        if (!dfn[a]) dfs(a);
    for (int a=1;a<=n;a++)
        if (belong[a]==belong[a+n]) return false;
    return true;
}

int main()
{
    readint(n);
    int minv=0x3f3f3f3f,maxv=-0x3f3f3f3f;
    int x=0;
    for (int a=1;a<=n;a++)
    {
        int v1,v2;
        readint(v1);
        readint(v2);
        z[++x]=rec(v1,a);
        z[++x]=rec(v2,a+n);
        minv=min(minv,min(v1,v2));
        maxv=max(maxv,max(v1,v2));
    }
    if (maxv-minv+1 < n)
    {
        printf("0\n");
        return 0;
    }
    sort(z+1,z+x+1);
    for (int a=1;a<=(n<<1);a++)
        pos[z[a].p]=a;
    int l=0,r=1000000001;
    while (l+1!=r)
    {
        int m=(l+r)>>1;
        if (check(m)) l=m;
        else r=m;
    }
    printf("%d\n",l);

    return 0;
}

 

 

 

思考題:

N 1,2,3,4...n

有m組邊 第i組邊是從i向li~ri連一個長度爲di的邊

求1~n的最短路

相關文章
相關標籤/搜索