cf 424

Office Keys

首先显然有随人位置的递增,钥匙的位置也要递增,这样考虑两张做法:

1.$f(i,j)$ 表示前i个人,钥匙到第j个最少用的最大时间,然后$O(nK)$ dp

2.二分时间,对于每一个人选择当前能选择的最左面的钥匙 $O((n+K) logn)$

#include <bits/stdc++.h>

#define LL long long

const int N = 2010;

using namespace std;

int n,m,pre[N],a[N],b[N];

int Abs(int x)
{
    if(x<0) return -x;
    return x;
}

int p;

bool check(int tim)
{
    int j = 1;
    for(int i=1;i<=n;i++)
    {
        while(j<=m && Abs(a[i]-b[j])+(LL)Abs(b[j]-p) > tim) j++;
        if(j>m) return 0;
        else j++;
    }
    return 1;
}

int main()
{
    int maxv = 0;
    scanf("%d%d%d",&n,&m,&p);
    maxv = p;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]), maxv = max(maxv ,a[i]);
    for(int i=1;i<=m;i++) scanf("%d",&b[i]), maxv = max(maxv, b[i]);
    sort(a+1,a+n+1);
    sort(b+1,b+m+1);
    int l=0, r=0;
    for(int i=1;i<=n;i++) l = max(l, Abs(p-a[i]));
    for(int i=1;i<=n;i++) r = max(r, Abs(b[i]-a[i]) + Abs(p-b[i]));
    while(r-l>5)
    {
        int mid = (l+(LL)r)>>1LL;
        if(check(mid)) r = mid;
        else l = mid;
    }
    for(int i=l;i<=r;i++)
        if(check(i))
        {
            cout<<i<<endl;
            break;
        }
    return 0;
}
View Code

 

Cards Sorting

方法1:直接用splay模拟。

方法2:考虑每次删掉当前最小数的代价是上一个最小数和它之间的循环dist,BIT或链表即可。

(取自CF)

 

#include <bits/stdc++.h>

const int N = 100010;
#define INF 0x3f3f3f3f
#define LL long long

using namespace std;

struct node
{
    node *ch[2];
    int v, sum, minv;
    int cmp(int x)
    {
        if(ch[0]->sum + 1 == x) return -1;
        return x > ch[0]->sum;
    }
    node* init(int x);
    void update()
    {
        sum = ch[0]->sum + ch[1]->sum + 1;
        minv = min(v, min(ch[0]->minv, ch[1]->minv));
    }
}spT[N], Null, *root;
 
int tot=0;
 
node* node::init(int x)
{
    v=x;
    minv=x;
    ch[0]=ch[1]=&Null;
    sum=1;
    return this;
}
 
node* build(int src[],int l,int r)
{
    if(l==r) return spT[++tot].init(src[l]);
    int mid=(l+r)>>1;
    node* tmp=spT[++tot].init(src[mid]);
    if(l<mid) tmp->ch[0]=build(src,l,mid-1);
    if(mid<r) tmp->ch[1]=build(src,mid+1,r);
    tmp->update();
    return tmp;
}
 
void rot(node* &o,int x)
{
    node* tmp=o->ch[x];
    o->ch[x]=tmp->ch[x^1];
    tmp->ch[x^1]=o;
    o->update();
    o=tmp;
    o->update();
}
 
void splay(node* &o,int k)
{
    int t=o->cmp(k);
    if(t==-1) return;
    if(t) k-=o->ch[0]->sum+1;
    int t1=o->ch[t]->cmp(k);
    if(~t1)
    {
        if(t1) k-=o->ch[t]->ch[0]->sum+1;
        splay(o->ch[t]->ch[t1],k);
        if(t1==t) rot(o,t);
        else rot(o->ch[t],t1);
    }
    rot(o,t);
}

int find_min(node *&o,int minv,int now)
{
    if(o->ch[0]->minv == minv) return find_min(o->ch[0],minv,now);
    else
    {
        if(o->v == minv) return now+o->ch[0]->sum;
        return find_min(o->ch[1],minv,now+o->ch[0]->sum+1);
    }
}

int n,a[N];

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    Null.minv = INF;
    a[0]=INF;
    a[n+1]=INF;
    root = build(a,0,n+1);
    int len = n+2;
    LL ans = 0;
    for(int i=1;i<=n;i++)
    {
        int tmp = find_min(root,root->minv,0);
        ans += (LL)tmp;
        splay(root,1);
        splay(root->ch[1],tmp+1);
        node* tp = root->ch[1]->ch[0];
        root->ch[1]->ch[0] = &Null;
        root->ch[1]->update();
        root->update();
        
        splay(tp,tp->sum);
        tp = tp->ch[0];
        len--;
        
        splay(root,len-tp->sum);
        splay(root->ch[0],len-tp->sum-1);
        root->ch[0]->ch[1] = tp;
        root->ch[0]->update();
        root->update();
    }
    cout << ans << endl;
    return 0;
}
View Code

 

Bamboo Partition

显然要满足的条件等价于

$\sum_{i=1}^n { [\frac{a_i + d - 1}{d}] \cdot d - a_i} \leq K$

$F(d) = \sum_{i=1}^n { [\frac{a_i - 1}{d}] + 1} \cdot d  \leq K + \sum {a_i}$

固定 $\sum_{i=1}^n { [\frac{a_i - 1}{d}] + 1}$,从而 $F(d)$ 单调。

对于前者直接找出所有得数相同的 $d$ 的区间,逐一计算即可。

复杂度$O(n^2 \sqrt {a_i} )$

通过调参可以 $O(n \sqrt {a_{max} n})$

#include <bits/stdc++.h>

#define LL long long

const int N = 110;

using namespace std;

int n,tot,a[N];
LL K;
vector<int> v;

int main()
{
    cin>>n>>K;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        K += (LL)a[i];
        int j=1;
        int tmp = a[i]-1;
        if(tmp>0)
        {
            for(int i=1;i<=tmp;i=j+1)
                j = tmp/(tmp/i), v.push_back(i);
            v.push_back(tmp+1);
        }
    }
    v.push_back(1);
    sort(v.begin(),v.end());
    v.resize(distance(v.begin(), unique(v.begin(), v.end())));
    LL ans = 1, sum;
    for(int i=0;i<(int)v.size() - 1;i++)
    {
        int l = v[i], r = v[i+1]-1;
        sum = 0;
        for(int j=1;j<=n;j++) sum += (a[j]-1LL)/l + 1LL;
        LL tmp = min(K/sum, (LL)r);
        if(tmp>=l) ans = tmp;
    }
    LL l = v[v.size()-1];
    LL tmp = K/n;
    if(tmp >= l) ans = tmp;
    cout << ans << endl;
}
View Code

 

posted @ 2017-07-14 21:42  lawyer'  阅读(188)  评论(0编辑  收藏  举报