2018年5月23日 星期三

(IOI) 2009 Day 2 p4 Salesman

https://contest.yandex.com/ioi/contest/1363/problems/H/

這題有個關鍵(?) : 所有的位置 (展覽 + 家) 都不一樣。

所以,先考慮 60 pts (沒有兩個展覽在同一天),這個可以用DP去實現。

dp[i] --> 在位置 i 當作最後一個 展覽,最佳的獲利是多少。

這樣一來, dp[i] = max( dp[j] + cost(i,j) ) , 其中 cost (i,j) 是 位置 i  到位置 j 的距離。

因為距離的cost有兩種:一種是 用 U 的,一種是用 D 的,所以要開兩個線段樹分別存下來(?

這樣,就可以在 O(N lg N) 的複雜度解決這題。

接下來,考慮 有多個展覽在同一天。

可以發現,對於最佳解,有一個固定的pattern是:先走到某一個展覽,在一直延那個展覽往右 / 往左走。

於是,好好地用上面那個線段樹搞一搞,就okay惹~

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

typedef pair<int,int> pii;
#define F first
#define S second

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

pii operator+(const pii &p1,const pii &p2)
{
    return make_pair(max(p1.F,p2.F),max(p1.S,p2.S));
}

const int INF = 2000000009;
const int POS_MAX = 500004;
const int POS_MIN = -1;

pii ee = make_pair(-INF,-INF);

struct Node
{
    Node *lc,*rc;
    pii val;
    Node():lc(NULL),rc(NULL),val(ee){}
    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);
    return node;
}

void modify(Node* node,int L,int R,int pos,pii 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->pull();
}

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

int dp[POS_MAX];

int t[POS_MAX],l[POS_MAX],m[POS_MAX];

vector<int> tt[POS_MAX];

int main ()
{
    int n,u,d,s;
    scanf("%d %d %d %d",&n,&u,&d,&s);
    ++s;
    int nn = 500003;
    Node* root = Build(1,nn);
    for (int i=1;n>=i;i++)
    {
        scanf("%d %d %d",&t[i],&l[i],&m[i]);
        ++l[i];
        tt[ t[i] ].push_back(i);
    }
    //swap(d,u);
    modify(root,1,nn,s, make_pair( dp[s]-(s-POS_MIN)*u , dp[s]-(POS_MAX-s)*d ) );
    for (int ii=1;nn>=ii;ii++)
    {
        //if (SZ(tt[ii]) != 1) continue;
        //cout << "ii = " << ii << endl;
        if (SZ(tt[ii]) >= 1)
        {
            for (int i:tt[ii])
            {
                pii p = query(root,1,nn,1,l[i]-1);
                pii q = query(root,1,nn,l[i]+1,nn);
                int mx = -INF;
                if (p.S != -INF)
                {
                    mx = max(mx,p.S + (POS_MAX-l[i])*d);
                }
                if (q.F != -INF)
                {
                    mx = max(mx,q.F + (l[i]-POS_MIN)*u);
                }
                dp[ l[i] ] = mx + m[i];
                //cout << "dp [ " << l[i] << " ] = " << dp[ l[i] ] << endl;
                //modify(root,1,nn,l[i], make_pair( dp[ l[i] ]-( l[i] -POS_MIN)*u , dp[ l[i] ]-(POS_MAX- l[i] )*d ) );
            }
            for (int i:tt[ii])
            {
                modify(root,1,nn,l[i], make_pair( dp[ l[i] ]-( l[i] -POS_MIN)*u , dp[ l[i] ]-(POS_MAX- l[i] )*d ) );
            }
        }
        if (SZ(tt[ii]) > 1)
        {
            sort(tt[ii].begin(),tt[ii].end(),[](const int &a,const int &b)
             {
                 return l[a] < l[b];
             });
            //starting from l --> r
            pii ptmp = query(root,1,nn,1,l[ tt[ii][0] ]-1);
            int lmax = ptmp.S;
            for (int iii=0;SZ(tt[ii])>iii;iii++)
            {
                int i = tt[ii][iii];
                dp[ l[i] ] = max( dp[ l[i] ], lmax + (POS_MAX - l[i])*d + m[i] );
                lmax += m[i];
                if (iii == SZ(tt[ii]) - 1) break;
                int nxti = tt[ii][iii+1];
                pii qtmp = query(root,1,nn,l[i],l[nxti]-1);
                lmax = max(lmax,qtmp.S);
            }
            //starting from r --> l
            reverse(tt[ii].begin(),tt[ii].end());
            ptmp = query(root,1,nn,l[ tt[ii][0] ]+1,nn);
            int rmax = ptmp.F;
            for (int iii=0;SZ(tt[ii])>iii;iii++)
            {
                int i = tt[ii][iii];
                dp[ l[i] ] = max( dp[ l[i] ], rmax + (l[i] - POS_MIN)*u + m[i]);
                rmax += m[i];
                if (iii == SZ(tt[ii]) - 1) break;
                int nxti = tt[ii][iii+1];
                pii qtmp = query(root,1,nn,l[nxti]+1,l[i]);
                rmax = max(rmax,qtmp.F);
            }
            /*reverse(tt[ii].begin(),tt[ii].end());
            for (int iii=1;SZ(tt[ii])>iii;iii++)
            {
                int i = tt[ii][iii];
                int prei = tt[ii][iii-1];
                dp[i] = max(dp[i],dp[prei] + (l[i] - l[prei])*d);
            }
            reverse(tt[ii].begin(),tt[ii].end());
            for (int iii=1;SZ(tt[ii])>iii;iii++)
            {
                int i = tt[ii][iii];
                int prei = tt[ii][iii-1];
                dp[i] = max(dp[i],dp[prei] + (l[prei] - l[i])*u);
            }*/
            for (int i:tt[ii])
            {
                modify(root,1,nn,l[i], make_pair( dp[ l[i] ]-( l[i] -POS_MIN)*u , dp[ l[i] ]-(POS_MAX- l[i] )*d ) );
            }
        }
    }
    //swap(d,u);
    int ans = 0;
    for (int i=1;POS_MAX>i;i++)
    {
        /*if (dp[i])
        {
            cout << "i = " << i << " , dp = " <<dp[i] << endl;
        }*/
        if (i < s)
        {
            ans = max(ans,dp[i] - (s-i)*d);
        }
        else
        {
            ans = max(ans,dp[i] - (i-s)*u);
        }
    }
    printf("%d\n",ans);
}

(IOI) 2009 Day 2 p3 Regions

https://contest.yandex.com/ioi/contest/1363/problems/G/

首先,可以先把長官關係轉化成一顆樹,並且給定尤拉序列(Euler tour)。

所以,接下來詢問就變成:有多少區間pair(x,y),使得區間 x 屬於 r1,區間 y屬於 r2,並且r1完整包覆r2。

到這邊,可以先提出很多種算法,到時候再一起併起來之類的(?)

 定義 S1 是 r1 集合的大小, S2 是 r2集合的大小

(1) 預處理答案

預處理所有的 (r1,r2) 組合,注意到對於一個r1,可以在O(N)的時間 (走訪這棵樹) 求出所有 r2的答案。

(2)  O(S1 + S2)

如果 r1 和 r2 是已經排好序的話,就可以好好的使用類似雙指針 (類似而已,實作細節可參考底下的query3() )。

(3) O(S1 log S2) or O(S2 log S1)

好好的寫XD。

對於每個 r ,先按照尤拉序列走訪後的區間排序,之後開個持久化線段樹,好好的想辦法讓每次的問題都做到 log 的查詢時間

這樣一來,有兩種併法:

(一)

(1) + (2)
如果 S1, S2 其中一個 > C,使用 (1),否則使用(2)
取C = N^0.5的話,分析複雜度後會是好的。

(二)

(2) + (3)  //下面code的寫法

如果S1, S2其中一個 > C,使用(3) ,選擇複雜度比較小的那個
否則使用 (2)

適當的取C,會是好的XDDD。

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

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

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;
}

Node* getNode(Node* old)
{
    Node* node = new Node();
    node->lc = old->lc;
    node->rc = old->rc;
    node->sum = old->sum;
    return node;
}

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

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->sum;
    int mid=(L+R)>>1;
    return query(node->lc,L,mid,l,r) + query(node->rc,mid+1,R,l,r);
}

typedef pair<int,int> pii;
const int N = 200006;

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

vector<int> G[N];
vector<pii> rr[N];
int r_pos[N];



#define F first
#define S second

pii p[N];

int stamp;

void dfs(int now)
{
    p[now].F = (++stamp);
    for (int i:G[now])
    {
        dfs(i);
    }
    p[now].S = stamp;
}

Node* root[N];
int pre[N];

int n;

int query1(int x,int y)
{
    //how many interval cover y
    //size of y is smaller
    int ret=0;
    int lastpos = 0;
    for (pii p:rr[y])
    {
        int pos = lower_bound(rr[x].begin() + lastpos,rr[x].end(),p) - rr[x].begin();
        lastpos = pos;
        if (pos)
        {
            ret += query(root[ pre[x]+pos ],1,n,p.S,n);
        }
    }
    return ret;
}

int query2(int x,int y)
{
    //how many interval within y
    //size of y is smaller
    //cout << "X = " << x << " , y = " <<y <<endl;
    int ret=0;
    int lastpos=0;
    for (pii p:rr[y])
    {
        int pos = lower_bound(rr[x].begin()+lastpos,rr[x].end(),p) - rr[x].begin();
        //cout << " p = " << p.F <<" , " << p.S << " , pos = " << pos << endl;
        lastpos = pos;
        if (pos != 0)
        {
            //cout << "pos = " << pos << " , pre[x] = " << pre[x] << " , pre[x+1] = " << pre[x+1] <<endl;
            ret += (query(root[ pre[x+1] ],1,n,p.F,p.S) - query(root[ pre[x]+pos ],1,n,p.F,p.S));
        }
        else
        {
            ret += query(root[ pre[x+1] ],1,n,p.F,p.S);
        }
    }
    return ret;
}

int query3(int x,int y)
{
    int ptrl = 0;
    int ptrr = -1;
    int ret=0;
    for (int i=0;SZ(rr[x])>i;i++)
    {
        while (ptrl != SZ(rr[y]) && rr[y][ptrl].F < rr[x][i].F) ++ptrl;
        while (ptrr != -1 && rr[y][ptrr].F > rr[x][i].S) --ptrr;
        while (ptrr != SZ(rr[y])-1 && rr[y][ptrr+1].F <= rr[x][i].S) ++ptrr;
        ret += (ptrr - ptrl + 1);
    }
    return ret;
}

const int C = 706;

int main ()
{
    int r,q;
    scanf("%d%d%d",&n,&r,&q);
    for (int i=1;n>=i;i++)
    {
        if (i == 1)
        {
            int x;
            scanf("%d",&x);
            //rr[x].push_back(i);
            r_pos[i] = x;
            continue;
        }
        int par,x;
        scanf("%d%d",&par,&x);
        G[par].push_back(i);
        //rr[x].push_back(i);
        r_pos[i] = x;
    }
    dfs(1);
    for (int i=1;n>=i;i++)
    {
        //cout << "i = " <<i << " , r_pos = " << r_pos[i] << " , p = " << p[i].F << " , " << p[i].S << endl;
        rr[ r_pos[i] ].push_back(p[i]);
    }
    for (int i=1;r>=i;i++)
    {
        sort(rr[i].begin(),rr[i].end());
        pre[i+1] = pre[i] + SZ(rr[i]);
        //cout << "i = " <<i << " , pre = " << pre[i] <<endl;
    }
    int id=1;
    root[0] = Build(1,n);
    for (int i=1;r>=i;i++)
    {
        if (SZ(rr[i]) == 0) continue;
        root[id] = getNode(root[0]);
        modify(root[0],root[id],1,n,rr[i][0].S,1);
        ++id;
        //cout << "hi1" <<endl;
        for (int j=1;SZ(rr[i])>j;j++)
        {
            //cout << "jj = " << j << endl;
            root[id] = getNode(root[id-1]);
            modify(root[id-1],root[id],1,n,rr[i][j].S,1);
            ++id;
        }
        //cout << "i = " << i <<" , id = " << id << endl;
    }
    map<pii,int> mp;
    while (q--)
    {
        int r1,r2;
        scanf("%d%d",&r1,&r2);
        if (SZ(rr[r1]) == 0 ||SZ(rr[r2]) == 0)
        {
            printf("0\n");
            fflush(stdout);
            continue;
        }
        if (mp.find(make_pair(r1,r2)) != mp.end())
        {
            printf("%d\n",mp[ make_pair(r1,r2) ]);
            fflush(stdout);
            continue;
        }
        int ans = 0;
        if (SZ(rr[r1]) < C && SZ(rr[r2]) < C)
        {
            ans = query3(r1,r2);
        }
        else if (SZ(rr[r1]) >= SZ(rr[r2]))
        {
            ans = query1(r1,r2);
        }
        else
        {
            ans = query2(r2,r1);
        }
        mp[ make_pair(r1,r2) ] = ans;
        printf("%d\n",ans);
        fflush(stdout);
    }
}


(IOI) 2009 Day 2 p2 Mecho

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

BFS + 二分搜答案

注意到二分搜的範圍要夠大喔 (可以想想極端case XD,被這個卡超久的)

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

typedef pair<int,int> pii;

#define F first
#define S second

int mp[806][806];
int bee_dis[806][806];

int dx[4] = {1,0,-1,0} , dy[4] = {0,1,0,-1};

int n,s;
pii st;
pii ed;

int dis[806][806];

bool can(int mid)
{
    queue<pii> que;
    memset(dis,-1,sizeof(dis));
    assert(st != make_pair(0,0));
    if (bee_dis[st.F][st.S] == -1) assert(0);
    if (bee_dis[st.F][st.S] <= mid) return false;
    dis[st.F][st.S] = 0;
    que.push(st);
    while (!que.empty())
    {
        pii p=que.front();
        que.pop();
        //cout << "p = " << p.F << " , " << p.S<<
        for (int i=0;4>i;i++)
        {
            int tx=p.F+dx[i];
            int ty=p.S+dy[i];
            if (make_pair(tx,ty) == ed) return true;
            if (mp[tx][ty] == 0 || dis[tx][ty] != -1) continue;
            if (bee_dis[tx][ty] == -1)
            {
                dis[tx][ty] = dis[p.F][p.S] + 1;
                que.push(make_pair(tx,ty));
            }
            else
            {
                int want_dis = dis[p.F][p.S] + 1;
                int del = 0;
                if (want_dis % s == 0)
                {
                    del = 0;
                }
                if (want_dis/s + del + mid < bee_dis[tx][ty])
                {
                    dis[tx][ty] = want_dis;
                    que.push(make_pair(tx,ty));
                }
            }
        }
    }
    return false;
}

int main ()
{
    cin >> n >> s;
    vector<pii> bees;
    for (int i=1;n>=i;i++)
    {
        string ss;
        cin >>ss;
        for (int j=1;n>=j;j++)
        {
            char c = ss[j-1];
            if (c == 'T') continue;
            if (c == 'G')
            {
                mp[i][j] = 1;
            }
            else if (c == 'M')
            {
                st = make_pair(i,j);
                mp[i][j] = 1;
            }
            else if (c == 'D')
            {
                ed = make_pair(i,j);
                mp[i][j] = 2;
            }
            else if (c == 'H')
            {
                bees.push_back(make_pair(i,j));
                mp[i][j] = 1;
            }
        }
    }
    memset(bee_dis,-1,sizeof(bee_dis));
    queue<pii> que;
    for (pii p:bees)
    {
        que.push(p);
        bee_dis[p.F][p.S] = 0;
    }
    while (!que.empty())
    {
        pii p=que.front();
        que.pop();
        for (int i=0;4>i;i++)
        {
            int tx=p.F+dx[i];
            int ty=p.S+dy[i];
            if (mp[tx][ty] == 1 && bee_dis[tx][ty] == -1)
            {
                que.push(make_pair(tx,ty));
                bee_dis[tx][ty] = bee_dis[p.F][p.S] +1;
            }
        }
    }
    if (!can(0))
    {
        cout << -1 << endl;
        return 0;
    }
    int L=0,R = 650212;
    while (R-L != 1)
    {
        int mid=(L+R)>>1;
        if (can(mid)) L = mid;
        else R = mid;
    }
    //assert(L != 20005);
    //assert(can(L) && !can(L+1));
    cout <<L << endl;
}

(IOI) 2009 Day 2 p1 Garage

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

implementation task (?

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

const int N = 5006;

int r[N];
int w[N];

int hhave[N];

int main ()
{
    int n,m;
    scanf("%d %d",&n,&m);
    //n --> parking space
    //m --> cars
    for (int i=1;n>=i;i++)
    {
        scanf("%d",&r[i]);
    }
    for (int i=1;m>=i;i++)
    {
        scanf("%d",&w[i]);
    }
    int ans=0;
    deque<int> wait;
    for (int i=1;2*m>=i;i++)
    {
        int x;
        scanf("%d",&x);
        if (x>0)
        {
            bool found = false;
            for (int j=1;n>=j;j++)
            {
                if (hhave[j] == 0)
                {
                    ans += r[j] * w[x];
                    hhave[j] = x;
                    found = true;
                    break;
                }
            }
            if (!found)
            {
                wait.push_back(x);
            }
        }
        else if (x < 0)
        {
            x = -x;
            for (int i=1;n>=i;i++)
            {
                if (hhave[i] == x)
                {
                    hhave[i] = 0;
                    if (!wait.empty())
                    {
                        hhave[i] = wait[0];
                        ans += w[ wait[0] ] * r[i];
                        wait.pop_front();
                    }
                }
            }
        }
    }
    printf("%d\n",ans);
}

(IOI) 2009 Day 1 p4 Raisins

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

先做二維前綴和之後好好DP即可。

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

const int N = 51;

int a[N][N];
int pre[N][N];
int dp[N][N][N][N];

int cal(int x1,int y1,int x2,int y2)
{
    return pre[x2][y2] - pre[x2][y1-1] - pre[x1-1][y2] + pre[x1-1][y1-1];
}

int solve(int x1,int y1,int x2,int y2)
{
    if (x1 == x2 && y1 == y2) return 0;
    if (dp[x1][y1][x2][y2] != -1) return dp[x1][y1][x2][y2];
    int &ret = dp[x1][y1][x2][y2];
    ret = 2147483647;
    int _ = cal(x1,y1,x2,y2);
    for (int i=x1;x2>i;i++)
    {
        ret = min(ret,_ + solve(x1,y1,i,y2) + solve(i+1,y1,x2,y2));
    }
    for (int j=y1;y2>j;j++)
    {
        ret = min(ret,_ + solve(x1,y1,x2,j) + solve(x1,j+1,x2,y2));
    }
    //cout << "x1 = " <<x1 << " , y1 = " <<y1 << " , x2 = " << x2 << " , y2 = " << y2 << " , ret = " << ret <<endl;
    return ret;
}

int main ()
{
    memset(dp,-1,sizeof(dp));
    int n,m;
    scanf("%d %d",&n,&m);
    for (int i=1;n>=i;i++)
    {
        for (int j=1;m>=j;j++)
        {
            scanf("%d",&a[i][j]);
        }
    }
    for (int i=1;n>=i;i++)
    {
        for (int j=1;m>=j;j++)
        {
            pre[i][j] = pre[i-1][j] + pre[i][j-1] - pre[i-1][j-1] + a[i][j];
        }
    }
    printf("%d\n",solve(1,1,n,m));
}

(IOI) 2009 Day 1 p3 POI

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

implementation task (?

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

typedef pair<int,int> pii;

bool adj[2002][2002];

int score[2002];

int main()
{
    int n,t,p,x;
    scanf("%d %d %d",&n,&t,&p);
    for (int i=1;t>=i;i++)
    {
        score[i] = n;
    }
    for (int i=1;n>=i;i++)
    {
        for (int j=1;t>=j;j++)
        {
            scanf("%d",&x);
            adj[i][j] = x;
            score[j] -= adj[i][j];
        }
    }
    vector< pair<pii,int> > v;
    for (int i=1;n>=i;i++)
    {
        int tot=0;
        int cnt=0;
        for (int j=1;t>=j;j++)
        {
            tot += adj[i][j] * score[j];
            cnt += adj[i][j];
        }
        v.push_back(make_pair(make_pair(-tot,-cnt),i));
        if (i == p)printf("%d ",tot);
    }
    sort(v.begin(),v.end());
    for (int i=1;n>=i;i++)
    {
        if (v[i-1].second == p)
        {
            printf("%d\n",i);
            return 0;
        }
    }
}

(IOI) 2009 Day 1 p2 Hiring

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

可以先用二分搜來確認人數。

人數確認好了後,可以好好的利用比例的關係(?)去做比較,就可以找出最小值的答案這樣。


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

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

#define F first
#define S second

pii p[N];
int v[N];
int n;
LL w;

vector<int> vv;

bool doo(int mid,bool get_ans = false)
{
    priority_queue<pii> pq;
    LL tot=0;
    for (int i=1;mid-1>=i;i++)
    {
        pq.push(make_pair(p[ v[i] ].S , v[i]) );
        tot += p[ v[i] ].S;
    }
    bool ret = false;
    pii ww = make_pair(w,1);
    int ii = -1;
    for (int i=mid;n>=i;i++)
    {
        if (p[ v[i] ].F * (tot + p[ v[i] ].S) * ww.S <= p[ v[i] ].S*ww.F)
        {
            ret = true;
            ii = i;
            ww = make_pair(p[ v[i] ].F * (tot + p[ v[i] ].S) , p[ v[i] ].S);
        }
        if (mid != 1 && p[ v[i] ].S < pq.top().F)
        {
            tot -= pq.top().F;
            pq.pop();
            pq.push(make_pair(p[ v[i] ].S,v[i]));
            tot += p[ v[i] ].S;
        }
    }
    if (get_ans)
    {
        while (!pq.empty()) pq.pop();
        tot=0;
        for (int i=1;mid-1>=i;i++)
        {
            pq.push(make_pair(p[ v[i] ].S , v[i]) );
            tot += p[ v[i] ].S;
        }
        for (int i=mid;n>=i;i++)
        {
            if (i == ii)
            {
                ret = true;
                vv.push_back(v[i]);
                while (!pq.empty())
                {
                    pii p=pq.top();
                    vv.push_back(p.S);
                    pq.pop();
                }
                return true;
            }
            if (mid != 1 && p[ v[i] ].S < pq.top().F)
            {
                tot -= pq.top().F;
                pq.pop();
                pq.push(make_pair(p[ v[i] ].S,v[i]));
                tot += p[ v[i] ].S;
            }
        }
    }
    return ret;
}

int main ()
{
    scanf("%d %lld",&n,&w);
    for (int i=1;n>=i;i++)
    {
        v[i] = i;
        scanf("%lld %lld",&p[i].F,&p[i].S);
    }
    sort(v+1,v+n+1,[&](const int &a,const int &b)
     {
         return p[a].F*p[b].S < p[a].S*p[b].F;
     })
    ;
    int L=0,R=n+1;
    //L is the answer
    while (R-L != 1)
    {
        int mid=(L+R)>>1;
        if (doo(mid)) L = mid;
        else R = mid;
    }
    printf("%d\n",L);
    doo(L,true);
    for (int i:vv) printf("%d\n",i);
}

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);
}