2018年5月18日 星期五

(IOI) 2008 Day 2 p3 Teleporters

https://contest.yandex.com/ioi/contest/567/problems/F/

超精湛的一題(?)

我們先把線段分成2N + 1塊,然後每次的teleport 就把兩個interval連在一起的感覺。

這樣一來,我們就得到很多連通塊了。

我們可以加M個新線段,每加一條新的線段時,可以:

(1) merge  兩個連通塊,並且讓那個連通塊多兩分。
(2) 在一個連通塊產生一個新的連通塊(分數為1),原本的連通塊分數也加1。
(3) 忘了,editorial裡面有寫><

只需要用到前面兩個喔~

複雜度:O(N+M + 排序)

#include <bits/stdc++.h>
using namespace std;

const int N = 2000002;

bitset<N> vis;

int has_port[N];
int e[N];

int cnt=0;

void dfs(int now)
{
    if (vis[now] || now == N-1) return;
    vis[now] = true;
    if (has_port[now])
    {
        ++cnt;
        int _ = now, __ = ( e[ has_port[now] ]^now );
        if (_ > __) swap(_,__);
        if (now == __) dfs(_+1);
        if (now == _) dfs(__+1);
    }
    else
    {
        dfs(now+1);
    }
}

int main ()
{
    int n;
    scanf("%d",&n);
    int m;
    scanf("%d",&m);
    for (int i=1;n>=i;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        e[i] = (x^y);
        has_port[x] = i;
        has_port[y] = i;
    }
    vector<int> v;
    int start = -1;
    for (int i=1;N>i;i++)
    {
        if (!vis[i])
        {
            cnt = 0;
            dfs(i);
            if (i == 1) start = cnt;
            else if (cnt) v.push_back(cnt);
        }
    }
    if (v.size() >= m)
    {
        sort(v.begin(),v.end());
        reverse(v.begin(),v.end());
        int ans=start;
        for (int i=0;m>i;i++)
        {
            ans += (v[i]+2);
        }
        printf("%d\n",ans);
    }
    else
    {
        int ans=start;
        for (int i:v)
        {
            ans += (i+2);
            --m;
        }
        if (m%2==0) ans += 2*m;
        else ans += 2*m-1;
        printf("%d\n",ans);
    }
}

(IOI) 2008 Day 2 p2 Pyramid Base

https://contest.yandex.com/ioi/contest/567/problems/E/

首先,答案的正方形的長度可以二分搜,所以,我們可以先把問題轉成:有沒有一個長度為X的正方形,可以成功地放進格子裡面。

那,我們也可以輕易地算出:題目的那P個矩形中,每一個矩形會影響到多少 " 左上角放在某個格子的正方形 " ,注意到現在正方形的長度已經被限制成X了。

那,有了這些之後,我們就可以使用掃描線囉~

你需要一顆線段樹,可以支援:
(1) 區間加值
(2) 區間減值
(3) 查詢整體的min。

如果掃到某一個時間,當下整體的min <= B,那就代表可以放。

這樣的複雜度是O(P * log P * log( min(n,m) )),最後一筆subtask過不了QAQ。

最後一筆有一個很神奇(?)的特性:B = 0。

首先,我們先定義一個一個函數 F(x1,x2):代表說,在 x 座標是介於 [x1, x2]的區域中,連續 y 座標都是空的最大長度是多少。

可以發現: F(x1,x2) >= F(x1-1, x2) && F(x1,x2) >= F(x1,x2+1)

可以發現,如果固定x1,當x2越大的時候,F(x1,x2)會越來越小

當x2固定時,隨著x1越來越大,F(x1,x2)會越來越大

於是,我們可以使用雙指針來維護x1,x2,如果當下F(x1,x2) >= x2-x1+1,代表我們找到一個邊長為x2-x1+1的三角形,並且可以移動x2的指針。

如果當下F(x1,x2) < x2 - x1 +1,我們就要移動x1的指針的。

所以,我們需要一個資料結構(線段樹),維護:
(1) 加入一個線段
(2) 刪除一個線段
(3) 查詢當前狀態的F值

可以用類似 " 區間連續最大和 " 的線段樹來解決。

//really hard QAQ
#include <bits/stdc++.h>
using namespace std;

typedef pair<int,int> Pt;
typedef pair<int,int> pii;
typedef pair<pii,pii> Rec;

#define F first
#define S second
#define MP make_pair
#define SZ(x) ((int)(x).size())

int n,m,b;
int p;
const int N = 400006;

Rec rr[N];
Rec rrr[N];
int c[N];

vector<pii> Event[800006];  //MP(+ or - , index(to get y range) )

struct Node
{
    static Node mem[4*200006];
    Node *lc,*rc;
    int mn,tag;
    Node():lc(NULL),rc(NULL),tag(0),mn(0){}
    void pull()
    {
        mn = min(lc->mn,rc->mn);
    }
} Node::mem[4*200006],*ptr = Node::mem;

Node* Build(int L,int R)
{
    Node* node = new(ptr++)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 push(Node* node,int L,int R)
{
    if (L==R) node->tag = 0;
    if (node->tag == 0)return;
    node->lc->mn += node->tag;
    node->rc->mn += node->tag;
    node->lc->tag += node->tag;
    node->rc->tag += node->tag;
    node->tag = 0;
    return;
}

void modify(Node* node, int L,int R,int l,int r,int del)
{
    if (l>R || L>r) return;
    else if (l<=L &&R<=r)
    {
        node->mn += del;
        node->tag += del;
        return;
    }
    push(node,L,R);
    int mid=(L+R)>>1;
    modify(node->lc,L,mid,l,r,del);
    modify(node->rc,mid+1,R,l,r,del);
    node->pull();
    return;
}

bitset<1000006> vis_x,vis_y,ee;

bool can_0(int len)
{
    if (len >= min(n,m)+1) return false;
    ptr = Node::mem;
    int nn = n-len+1, mm = m-len+1;
    int pp=0;
    vector<int> vx,vy;
    vis_x = ee;
    vis_y = ee;
    for (int i=1;p>=i;i++)
    {
        int x1 = rr[i].F.F - len+1;
        int y1 = rr[i].F.S - len+1;
        int x2 = rr[i].S.F;
        int y2 = rr[i].S.S;
        x1 = max(x1,1);
        y1 = max(y1,1);
        if (x1 <= nn && y1 <= mm)
        {
            x2 = min(x2,nn);
            y2 = min(y2,mm);
            //if (x1 == 1 && y1 == 1 && x2 == nn && y2 == mm) return false;
            rrr[++pp] = MP( MP(x1,y1) , MP(x2,y2) );
            if (!vis_x[x1]) vx.push_back(x1);
            vis_x[x1] = true;
            if (x2 + 1 <= nn && !vis_x[x2+1]) vx.push_back(x2+1);
            vis_x[x2+1] = true;
            if (!vis_y[y1]) vy.push_back(y1);
            vis_y[y1] = true;
            if (1 <= y1-1 && !vis_y[y1-1]) vy.push_back(y1-1);
            vis_y[y1-1] = true;
            if (!vis_y[y2]) vy.push_back(y2);
            vis_y[y2] = true;
            if (y2+1 <= mm && !vis_y[y2+1]) vy.push_back(y2+1);
            vis_y[y2+1] = true;
        }
    }
    if (pp == 0) return true;
    vx.push_back(1); vx.push_back(nn);
    vy.push_back(1); vy.push_back(mm);
    sort(vx.begin(),vx.end()); vx.resize(unique(vx.begin(),vx.end()) - vx.begin());
    sort(vy.begin(),vy.end()); vy.resize(unique(vy.begin(),vy.end()) - vy.begin());
    //y axis --> segment tree
    for (int i=0;SZ(vx)>=i;i++)
    {
        Event[i].clear();
    }
    for (int i=1;pp>=i;i++)
    {
        int x1=rrr[i].F.F;
        int pos = lower_bound(vx.begin(),vx.end(),x1) - vx.begin() + 1;
        Event[pos].push_back(make_pair(1,i));
        rrr[i].F.S = lower_bound(vy.begin(),vy.end(),rrr[i].F.S) - vy.begin() + 1;
        rrr[i].S.S = lower_bound(vy.begin(),vy.end(),rrr[i].S.S) - vy.begin() + 1;
    }
    //cout << "len = " << len << endl;
    int mmm = SZ(vy);
    Node* root = Build(1,mmm);
    for (int i=1;SZ(vx)>=i;i++)
    {
        for (pii p:Event[i])
        {
            if (p.F == -1) modify(root,1,mmm,rrr[ p.S ].F.S,rrr[ p.S ].S.S,-c[ p.S ]);
            else
            {
                modify(root,1,mmm,rrr[ p.S ].F.S,rrr[p.S].S.S, c[p.S] );
                int x2 = rrr[p.S].S.F;
                if (x2+1 <= nn)
                {
                    int _ = x2+1;
                    int pos = lower_bound(vx.begin(),vx.end(),_) - vx.begin() + 1;
                    Event[pos].push_back(make_pair(-1,p.S));
                }
            }
        }
        push(root,1,mmm);
        //cout << "i = " << i << " , len = " << len << " root->mn = " << root->mn << endl;
        if (root->mn <= b)
        {
            return true;
        }
    }
    return false;
    //return true;
}

void solve()
{
    for (int i=1;p>=i;i++)
    {
        scanf("%d %d %d %d %d",&rr[i].F.F,&rr[i].F.S,&rr[i].S.F,&rr[i].S.S,&c[i]);
    }
    int L=0,R=1000006;
    while (R-L != 1)
    {
        int mid=(L+R)>>1;
        //cout << "mid = " << mid << endl;
        if (can_0(mid)) L = mid;
        else R = mid;
    }
    printf("%d\n",L);
}

struct Sagiri
{
    Sagiri *lc,*rc;
    int Lmax,Rmax,midmax;
    int cnt;
    int L,R;
    Sagiri(){}
    Sagiri(int _L,int _R):L(_L),R(_R),Lmax(0),Rmax(0),midmax(0),cnt(0){}
    void pull()
    {
        if (cnt)
        {
            Lmax = Rmax = midmax = 0;
            return;
        }
        else if (L==R)
        {
            Lmax = Rmax = midmax = 1;
            return;
        }
        int mid=(L+R)>>1;
        Lmax = (lc->Lmax == mid-L+1 ? lc->Lmax + rc->Lmax : lc->Lmax);
        Rmax = (rc->Rmax == R-mid ? rc->Rmax + lc->Rmax : rc->Rmax);
        midmax = max( max( lc->midmax , rc->midmax ) , lc->Rmax + rc->Lmax );
    }
};

Sagiri* Buildd(int L,int R)
{
    Sagiri* sagiri = new Sagiri(L,R);
    if (L==R)
    {
        sagiri->pull();
        return sagiri;
    }
    int mid=(L+R)>>1;
    sagiri->lc = Buildd(L,mid);
    sagiri->rc = Buildd(mid+1,R);
    sagiri->pull();
    return sagiri;
}

void modifyy(Sagiri* sagiri,int L,int R,int l,int r,int del)
{
    if (l>R || L>r) return;
    else if (l<=L && R<=r)
    {
        sagiri->cnt += del;
        sagiri->pull();
        return;
    }
    int mid=(L+R)>>1;
    modifyy(sagiri->lc,L,mid,l,r,del);
    modifyy(sagiri->rc,mid+1,R,l,r,del);
    sagiri->pull();
    return;
}

const int P = 1000006;

vector<int> addd[P],subb[P];

int main ()
{
    scanf("%d %d %d",&n,&m,&b);
    scanf("%d",&p);
    if (p <= 30000)
    {
        solve();
        return 0;
    }
    Sagiri* root = Buildd(1,m);
    for (int i=1;p>=i;i++)
    {
        scanf("%d %d %d %d %d",&rr[i].F.F,&rr[i].F.S,&rr[i].S.F,&rr[i].S.S,&c[i]);
        addd[ rr[i].F.F ].push_back(i);
        subb[ rr[i].S.F ].push_back(i);
    }
    int ans=0;
    int L=1;
    for (int R=1;n>=R;R++)
    {
        for (int i:addd[R])
        {
            modifyy(root,1,m,rr[i].F.S,rr[i].S.S,1);
        }
        //cout << "L = " << L << " , R = " << R<<" , midmax = " << root->midmax << endl;
        if (root->midmax >= R-L+1) ans = max(ans,R-L+1);
        while (root->midmax < R-L+1)
        {
            for (int i:subb[L])
            {
                modifyy(root,1,m,rr[i].F.S,rr[i].S.S,-1);
            }
            ++L;
        }
    }
    printf("%d\n",ans);
}

(IOI) 2008 Day 2 p1 Linear Garden

https://contest.yandex.com/ioi/contest/567/problems/D/

一開始想說,拿最後四個字元去DP就好,結果發現根本假解QAQ

於是,只好換個想法:我們把字串畫在座標軸上,每多一個'L'字元就往上走,每多一個'P'字串就往下走,大概像下面那樣


 可以發現,一個合法的字串,最大值跟最低值的差不能超過2!

所以,合法的字串的(最低, 最高) 的種類就只有這五種:(-2,0) , (-1,1) , (-1,0) , (0,1) , (0,2)

這樣一來,就比較好分析了!

那,要算種類數的時候,當你遇到一個P,你就要算說:如果當下選L,之前會有多少個字串!

那,接下來,因為你的種類數已經是固定的,而且,你也可以知道,當你現在在某個種類的某個位置時,之後還會有哪些字串,這樣子下去算就 okay囉~。

複雜度是 O(N)

#include <bits/stdc++.h>
using namespace std;

#define int long long

typedef pair<int,int> pii;

const int N = 1000003;

#define F first
#define S second

int n,m;

//current, type, position

bitset<N> a;

int pow2[N];

int cur_mx = 0,cur_mn = 0;

int L[6] = {0,0,-1,0,-2,-1};
int R[6] = {0,1,0,2,0,1};

int cur;

bool can(int type)
{
    return L[type] <= min(cur_mn,cur+2) && max(cur_mx,cur+2) <= R[type];
}

int ask(int type,int cur,int pos)
{
    if (type == 1 && can(1))
    {
        //(0,1)
        if (0 <= cur && cur <= 1) return 1;
    }
    else if (type == 2 &&can(2))
    {
        //(-1,0)
        if (-1 <= cur && cur <= 0) return 1;
    }
    else if (type == 3 && can(3))
    {
        //(0,2)
        if (0 <= cur && cur <= 2) return pow2[ (n-pos)/2 ];
    }
    else if (type == 4 && can(4))
    {
        //(-2,0)
        if (-2 <= cur && cur <= 0) return pow2[ (n-pos)/2 ];
    }
    else if (type == 5 && can(5))
    {
        //(-1,1)
        if (-1 <= cur && cur <= 1) return pow2[ (n-pos+1)/2 ];
    }
    return 0;
}

bool real_can(int type,int pos)
{
    return (can(type) && ask(type,cur+2,pos) != 0);
}

main ()
{
    char c;
    scanf("%lld",&n);
    scanf("%lld",&m);
    getchar();
    pow2[0] = 1;
    for (int i=1;n>=i;i++)
    {
        pow2[i] = pow2[i-1] + pow2[i-1];
        if (pow2[i] >= m) pow2[i] -= m;
    }
    for (int i=1;n>=i;i++)
    {
        c = getchar();
        if (c == 'P') a[i] = true;
    }
    int ans=0;
    cur = 0;
    for (int i=1;n>=i;i++)
    {
        if (!a[i]) ++cur;
        else --cur;
        if (a[i])
        {
            if (max(cur+2,cur_mx) - cur_mn == 1) ans += (pow2[(n-i)/2] + pow2[ (n-i+1)/2 ] - 1 + m);
            else if (max(cur+2,cur_mx) - cur_mn == 2) ans += (pow2[ (n-i)/2 ]);
            while(ans >= m) ans-=m;
        }
        cur_mx = max(cur_mx,cur);
        cur_mn = min(cur_mn,cur);
        //cout << "i = " << i << " , ans = " << ans << endl;
    }
    printf("%lld\n",(ans%m+m+1)%m);
}

2018年5月15日 星期二

(IOI) 2008 Day 1 p3 Fish

https://contest.yandex.com/ioi/contest/567/problems/C/

劇難(?

首先,先有個想法,對於相同顏色 (題目的魚口中含的那個東西),假設有一個長度是a,另外一個是長度b,假設a>b,那,b是最大長度的集合,a是最大長度集合也一定可以完全包含,所以,對於那k種顏色,我們只要考慮長度最大的就好惹。

那,接下來,我們考慮以下算法:
(1)挑出最長的東西
(2)算出如果選了那個東西,可以造成多少集合
(3)之後再算的時候,把這個顏色固定挑0個!

上面那樣看起來是對的,但是.....

6
4
30000
6 4
9 1
8 2
13 3
2 1
18 4

對於上面那組測資,如果實現上面那樣的算法,總集合數量只會17,正確答案是20。

研究之後發現,漏考慮的三個集合分別是:
[1,2] (挑選第四隻和第五隻)
[1, 3, 4] (挑選1,4,5)
[3, 4]  (挑選1,4)

為了方便說明,我定義一些函數:
POS(i,j) --> 第 i 種顏色的第 j 小的長度
BEST(i) --> 第 i 種顏色最長的長度

要怎麼把漏掉的case考慮進去呢(?)
可以發現,我們漏掉的case是:當有兩個顏色 x , y ,BEST(x) <= BEST(y) ,但是當 顏色 y 再用 BEST(y) 吃掉顏色 x 時,吃到的數量比用 BEST(x) 吃到的數量還要少 (假設 BEST(y)吃掉m個顏色x , BEST(x)就可以吃到m + 1(自己) 個 )。 所以,要想盡辦法把上面那種case考慮進去~

#include <bits/stdc++.h>
using namespace std;

const int N = 500006;

typedef pair<int,int> pii;

#define F first
#define S second
#define SZ(x) ((int)(x).size())

int mod;

int cnt[N];

struct Node
{
    Node *lc,*rc;
    int val;
    Node():lc(NULL),rc(NULL),val(1){}
    void pull()
    {
        val = (lc->val*rc->val)%mod;
    }
};

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

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

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

int query(Node* node,int L,int R,int l,int r)
{
    if (l>R || L>r) return 1;
    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)) % mod;
}

pii p[N];

vector<int> G[N];  //store length

bool vis[N];

int id[N];  //color position at segment tree
int pos[N];
int clr[N];

int main ()
{
    int n,k;
    scanf("%d",&n);
    scanf("%d",&k);
    scanf("%d",&mod);
    for (int i=1;n>=i;i++)
    {
        scanf("%d %d",&p[i].F,&p[i].S);
    }
    sort(p+1,p+n+1);
    int ptr=0;
    for (int i=1;n>=i;i++)
    {
        G[ p[i].S ].push_back(p[i].F);
        if (p[i].F * 2 <= p[n].F)
        {
            cnt[ p[i].S ]++;
            ptr = i;
        }
    }
    Node* root1 = Build(1,k);
    Node* root2 = Build2(1,k);
    int ans=0;
    int nn=0;
    int dead_mn = 1000000007;
    for (int i=n;i>=1;i--)
    {
        if (vis[ p[i].S ]) continue;
        while (ptr > 0 && p[ptr].F*2 > p[i].F)
        {
            if (vis[ p[ptr].S ])
            {
                modify(root2,1,k,id[p[ptr].S],-1);
            }
            else
            {
                modify(root1,1,k,p[ptr].S,-1);
            }
            --cnt[ p[ptr].S ];
            //if (vis[ p[ptr].S ] && cnt[ p[ptr].S ] == 0) modify(root2,1,k,id[ p[ptr].S ],0,true);
            --ptr;
        }
        vis[ p[i].S ] = true;
        //cout << "i = " << i << endl;
        //cout << "ans += " << root1->val << endl;
        ans += root1->val;
        ans %= mod;
        modify(root1,1,k,p[i].S,0,true);
        //some searching
        if (nn != 0)
        {
            int now_cnt_clr = cnt[ p[i].S ]+1;
            //cout << "now_cnt_clr = " << now_cnt_clr << endl;
            int rr = 2* G[ p[i].S ][ now_cnt_clr-1 ] -1;
            //cout << "rr = " << rr << endl;
            if (rr >= pos[nn])
            {
                int L=0,R=nn;
                //R is the answer
                while (R-L != 1)
                {
                    int mid=(L+R)>>1;
                    if (rr >= pos[mid]) R = mid;
                    else L = mid;
                }
                //cout << "R = " << R << endl;
                //cout << "ans2 += " << query(root1,1,k,1,p[i].S-1) *1LL * query(root1,1,k,p[i].S+1,k) * query(root2,1,k,R,k) % mod << endl;
                ans += (query(root1,1,k,1,p[i].S-1) *1LL * query(root1,1,k,p[i].S+1,k) * (query(root2,1,k,R,k) -1LL+mod) % mod);
                ans %= mod;
            }
        }
        //final adding
        ++nn;
        clr[nn] = p[i].S;
        id[ p[i].S ] = nn;
        pos[nn] = p[i].F;
        modify(root2,1,k,id[ p[i].S ],cnt[ p[i].S ]);  //cannot select 0
        //modify(root1,1,k,p[i].S,0,true);
        if (cnt[ p[i].S ] == 0) modify(root2,1,k,id[ p[i].S ],0,true);
        dead_mn= min(dead_mn,G[ p[i].S ][0]);
    }
    printf("%d\n",ans);
}

(IOI) 2008 Day 1 p2 Islands

https://contest.yandex.com/ioi/contest/567/problems/B/

剛好之前在TIOJ寫過(?

每個連通塊的答案有兩種,一種是不經過環的,一種是經過環的。

對於沒有經過環的,好好DP一下就好(? (隨時維護最長鍊並且更新(?))

對於有經過環的,是個deque優化的DP (?)


#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<LL,int> pii;
const int N = 1000006;

#define F first
#define S second

#define SZ(x) ((int)(x).size())

vector<pii> G[N];

char vis[N];

LL dp[N];

vector<int> v;

int deg[N];

void get_all(int now)
{
    vis[now] = true;
    v.push_back(now);
    for (pii p:G[now])
    {
        if (!vis[p.F])
        {
            get_all(p.F);
        }
    }
}

int id[2*N];
int len[2*N];
LL pre[2*N];

LL solve(int x)
{
    v.clear();
    get_all(x);
    queue<int> que;
    LL ret=0;
    for (int i:v)
    {
        if (deg[i] == 1)
        {
            que.push(i);
        }
    }
    while (!que.empty())
    {
        int t=que.front();
        que.pop();
        vis[t] = 2;
        for (pii p:G[t])
        {
            if (vis[p.F] == 2) continue;
            ret = max(ret,dp[p.F] + dp[t] + p.S);
            dp[p.F] = max(dp[p.F],dp[t] + p.S*1LL);
            deg[p.F]--;
            if (deg[p.F] == 1) que.push(p.F);
        }
    }
    vector<int> cycle;
    for (int i:v)
    {
        ret = max(ret,dp[i]);
        if (vis[i] == 1 && SZ(cycle) == 0)
        {
            int now=i;
            bool chg = false;
            do {
                cycle.push_back(now);
                //cout << "before now = " << now << " , ";
                vis[now] = 3;
                chg = false;
                //cout << "vis = " << vis[now] << endl;
                for (pii p:G[now])
                {
                    if (vis[p.F] == 1)
                    {
                        now = p.F;
                        chg = true;
                        break;
                    }
                }
                //cout << "after now = " << now << endl;
            } while (now != i && chg);
        }
    }
    //cout << "x = " << x << " , sz cycle = " << SZ(cycle) << endl;
    if (SZ(cycle) == 2)
    {
        for (int i:cycle)
        {
            for (pii p:G[i])
            {
                if (vis[p.F] == 3)
                {
                    ret = max(ret,dp[i] + dp[p.F] + p.S);
                }
            }
        }
        return ret;
    }
    int n = SZ(cycle);
    cycle.push_back(cycle[0]);
    for (int i=0;n>i;i++)
    {
        id[i+1] = cycle[i];
        for (pii p:G[ cycle[i] ])
        {
            if (p.F == cycle[i+1])
            {
                len[i+1] = p.S;
                break;
            }
        }
    }
    for (int i=1;n>=i;i++)
    {
        //cout << "i = " << i << " , id = " << id[i] << " , len = " << len[i] << endl;
        //dp_id[i] = dp[ id[i] ];
        id[i+n] = id[i];
        len[i+n] = len[i];
        //dp_id[i+n] = dp_id[i];
    }
    for (int i=1;2*n>=i;i++)
    {
        pre[i] = pre[i-1] + len[i];
    }
    deque<pii> dq;
    //pii --> value(dp_id[id]), id
    for (int i=1;2*n>=i;i++)
    {
        if (SZ(dq) == 0)
        {
            ret = max(ret,dp[ id[i] ]);
        }
        else
        {
            while ( SZ(dq) && dq.front().S <= i-n) dq.pop_front();
            ret = max(ret,dp[ id[i] ] + dq.front().F + pre[i-1] - pre[ dq.front().S-1 ]);
        }
        pii now = make_pair(dp[ id[i] ],i);
        while (SZ(dq) && pre[i-1] - pre[ dq.back().S-1 ] + dq.back().F <= now.F) dq.pop_back();
        dq.push_back(now);
    }
    return ret;
}

int main ()
{
    int n;
    scanf("%d",&n);
    for (int i=1;n>=i;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        deg[x]++;
        deg[i]++;
        G[x].push_back(make_pair(i,y));
        G[i].push_back(make_pair(x,y));
    }
    LL ans=0;
    for (int i=1;n>=i;i++)
    {
        if (!vis[i])
        {
            ans += solve(i);
        }
    }
    printf("%lld\n",ans);
}

(IOI) 2008 Day 1 p1 Type Printer

https://contest.yandex.com/ioi/contest/567/problems/A/

把Trie建出來之後開始DFS,最長鍊最後拜訪,拜訪完之後就不要回到 root (相當於留一些letter在printer裡面)


#include <bits/stdc++.h>
using namespace std;

const int SIGMA = 26;
const int N = 500006;

#define SZ(x) ((int)(x).size())

vector<char> ans;
vector<int> G[N];

int sz=1;
int ch[N][SIGMA];

bool is_end[N];

string add_str;

char id[N];

int idx(char c)
{
    return c-'a';
}

void addd(int now,int add_n)
{
    if (add_n == SZ(add_str))
    {
        is_end[now] = true;
        return;
    }
    int c = idx(add_str[add_n]);
    if (!ch[now][c]) ch[now][c] = ++sz;
    addd(ch[now][c],add_n+1);
}

void build_graph(int now)
{
    for (int i=0;SIGMA>i;i++)
    {
        if (ch[now][i])
        {
            G[now].push_back( ch[now][i] );
            id[ ch[now][i] ] = i+'a';
            build_graph(ch[now][i]);
        }
    }
}

int mx_depth[N];

void dfs(int now,int cur_depth)
{
    mx_depth[now] = cur_depth;
    for (int i:G[now])
    {
        dfs(i,cur_depth+1);
        mx_depth[now] = max(mx_depth[now],mx_depth[i]);
    }
}

int n;

int vis_tot=0;

void solve(int now)
{
    sort(G[now].begin(),G[now].end(),[](const int &a,const int &b)
     {
         return mx_depth[a] < mx_depth[b];
     });
    if (now != 1) ans.push_back(id[now]);
    vis_tot += is_end[now];
    if (is_end[now]) ans.push_back('P');
    for (int i:G[now])
    {
        solve(i);
    }
    if (vis_tot != n) ans.push_back('-');
}

int main ()
{
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n;
    int root = 1;
    for (int i=0;n>i;i++)
    {
        string s;
        cin >> s;
        add_str = s;
        addd(root,0);
    }
    build_graph(root);
    dfs(1,1);
    solve(1);
    cout << (int)ans.size() << '\n';
    for (char i:ans)
    {
        cout << i << '\n';
    }
}

2018年5月4日 星期五

(TIOJ) 2039 . AI-666 賺多少 [GREEDY]

https://tioj.ck.tp.edu.tw/problems/2039

先說,下面是82分的 N log N greedy解。

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

typedef pair<LL,LL> pii;
const int N = 2000006;
const LL INF = (1LL<<50);

LL a[N];

#define SZ(x) ((int)(x).size())
#define F first
#define S second

LL s[N];

int lc[N];
int rc[N];

LL cost[N];

bool dead[N];

int main ()
{
    int n,k;
    scanf("%d %d",&n,&k);
    vector<LL> v;
    for (int i=1;n>=i;i++)
    {
        scanf("%lld",&a[i]);
        if (i > 1)
        {
            int _ = a[i] - a[i-1];
            if (_ > 0)
            {
                if (v.empty())
                {
                    v.push_back(_);
                }
                else if (v.back() > 0)
                {
                    v[ SZ(v)-1 ] += _;
                }
                else if (v.back() < 0)
                {
                    v.push_back(_);
                }
            }
            else if (_ < 0)
            {
                if (v.empty()) ;
                else if (v.back() > 0)
                {
                    v.push_back(_);
                }
                else if (v.back() < 0)
                {
                    v[ SZ(v)-1 ] += _;
                }
            }
        }
    }
    if (!v.empty() && v.back() < 0) v.pop_back();
    long long tot=0;
    int b=0;
    for (int i:v)
    {
        if (i>0)
        {
            tot += i;
            ++b;
        }
    }
    if (b<=k)
    {
        printf("%lld\n",tot);
        return 0;
    }
    int nn = SZ(v);
    s[0] = s[nn+1] = 0;
    for (int i=1;nn>=i;i++)
    {
        s[i] = abs(v[i-1]);
        lc[i] = i-1;
        rc[i] = i+1;
        cost[i] = s[i];
    }
    priority_queue<pii,vector<pii>,greater<pii> > pq;
    for (int i=1;nn>=i;i++)
    {
        pq.push(make_pair(cost[i],i));
        //cout << "cost = " << cost[i] << " , i = " << i << endl;
    }
    while (!pq.empty() && b > k)
    {
        pii p=pq.top();
        pq.pop();
        if (dead[p.S]) continue;
        //cout << "p = (" << p.F<< " , " << p.S << " )" << endl;
        --b;
        tot -= p.F;
        int mid = p.S;
        int l = lc[mid];
        int r = rc[mid];
        cost[mid] = cost[l] + cost[r] - cost[mid];
        if (l != 0 && r != nn+1) pq.push(make_pair(cost[mid],mid));
        else dead[mid] = true;
        dead[l] = true;
        dead[r] = true;
        lc[mid] = lc[l];
        rc[mid] = rc[r];
        //cout << "l = " << l << " , r = " << r << endl;
        rc[ lc[l] ] = (r != nn+1?mid:nn+1);
        lc[ rc[r] ] = (l != 0?mid:0);
        /*
        for (int i=1;nn>=i;i++)
        {
            cout << "i = " << i << " , cost = " <<cost[i] << " , dead = " << dead[i] << " , lc = " << lc[i] << " , rc = " << rc[i] << endl;
        }*/

        //cout << endl << endl;
    }
    printf("%lld\n",tot);
}