Codeforces Round #222 (Div. 1) (ABCDE)

377A Maze

大意: 给定棋盘, 保证初始所有白格连通, 求将$k$个白格变为黑格, 使得白格仍然连通.

$dfs$回溯时删除即可. 

#include <iostream>
#include <functional>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <math.h>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <string.h>
#include <bitset>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head



const int N = 1e3+10;
int n,m,s,px,py;
int vis[N][N];
char a[N][N];
const int dx[]={0,0,-1,1};
const int dy[]={-1,1,0,0};

int main() {
    scanf("%d%d%d", &n, &m, &s);
    REP(i,1,n) cin>>a[i]+1;
    REP(i,1,n) REP(j,1,m) if (a[i][j]=='.') px=i,py=j;
    function<void(int,int)> dfs = [&](int x, int y) {
        if (x<=0||y<=0||x>n||y>m||vis[x][y]||a[x][y]=='#') return;
        vis[x][y] = 1;
        REP(k,0,3) dfs(x+dx[k],y+dy[k]);
        if (s) --s,a[x][y]='X';
    };
    dfs(px,py);
    REP(i,1,n) puts(a[i]+1);
}
View Code

377B Preparing for the Contest 

大意: $m$道题, $n$个学生, 每个学生每天做一道题, 每个学生只能做难度不超过他的能力的题, 雇第$i$个学生做题需要花费$j$元, 求最短时间做完所有题的情况下的最少花费, 输出方案.

二分答案, 维护一个堆贪心

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <functional>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}

int main() {
    int n=rd(),m=rd(),s=rd();
    vector<pii> f(m);
    int id = 0;
    for(auto &t:f) t.x=rd(),t.y=id++;
    sort(begin(f),end(f),greater<pii>());
    vector<pair<int,pii> > g(n);
    id = 0;
    for(auto &t:g) t.x=rd();
    for(auto &t:g) t.y.x=rd(),t.y.y=++id;
    sort(begin(g),end(g),greater<pair<int,pii>>());
    vector<int> ans(m);
    auto chk = [&](int x) {
        auto now = g.begin();
        auto pos = f.begin();
        int sum = 0;
        priority_queue<pii,vector<pii>,greater<pii>> q;
        while (pos!=f.end()) {
            while (now!=g.end()&&now->x>=pos->x) q.push((now++)->y);
            if (q.empty()) return 0;
            auto mi = q.top(); q.pop();
            if (!mi.y||sum+mi.x>s) return 0;
            sum += mi.x;
            int t = x;
            while (pos!=f.end()&&t--) ans[(pos++)->y]=mi.y;
        }
        return 1;
    };
    int l=1,r=m,ret=-1;
    while (l<=r) {
        int mid = (l+r)/2;
        if (chk(mid)) ret=mid,r=mid-1;
        else l=mid+1;
    }
    if (ret==-1) return cout<<"NO"<<endl,0;
    cout<<"YES"<<endl;
    chk(ret);
    for (auto &t:ans) cout<<t<<' ';
    cout<<endl;
}
View Code

377C Captains Mode

大意: $n$个英雄, 给出两个队伍的操作序列, 操作$(p,x)$表示第$x$队选一个英雄, 操作$(b,x)$表示第$x$队禁一个英雄. 一个英雄被选过或被禁过就不能再选, 禁英雄操作可以跳过. 第一个队想要最大化两个队英雄属性和的差, 第二个队想要最小化. 求最优情况下的差值.

只需考虑属性前$m$大的英雄即可, 那么很容易有$O(m^22^m)$的$dp$

但是可以注意到禁英雄一定会比不禁英雄更优, 那么每个阶段可用英雄数是固定的, 所以第$i$个阶段状态数是$\binom{m}{m-i}$, 这样复杂度可以达到$O(m2^m)$.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head



const int N = 2e6+10;
int n, m, a[N], f[N];
int dp[N];

void chkmin(int &a, int b) {a>b?a=b:0;}
void chkmax(int &a, int b) {a<b?a=b:0;}
int main() {
    scanf("%d", &n);
    REP(i,0,n-1) scanf("%d",a+i);
    scanf("%d", &m);
    sort(a,a+n,greater<int>());
    REP(i,0,m-1) { 
        char op;
        int x;
        scanf(" %c%d", &op, &x);
        f[i] = (op=='p')<<1|(x==1);
    }
    int mx = (1<<m)-1;
    REP(S,1,mx) {
        int i = m-__builtin_popcount(S);
        int &r = dp[S];
        if (f[i]==0) {
            r = INF;
            REP(j,0,m-1) if (S>>j&1) {
                chkmin(r,dp[S^1<<j]);
            }
        }
        else if (f[i]==1) {
            r = -INF;
            REP(j,0,m-1) if (S>>j&1) {
                chkmax(r,dp[S^1<<j]);
            }
        }
        else if (f[i]==2) {
            r = INF;
            REP(j,0,n-1) if (S>>j&1) {
                chkmin(r,dp[S^1<<j]-a[j]);
            }
        }
        else if (f[i]==3) {
            r = -INF;
            REP(j,0,n-1) if (S>>j&1) {
                chkmax(r,dp[S^1<<j]+a[j]);
            }
        }
    }
    printf("%d\n",dp[mx]);
}
O(m2^m) 

377D Developing Game

大意: $n$个工人, 第$i$个工人属性$v_i$, 只能和属性范围$[L_i,R_i]$的人一起工作, 求最多选出多少工人.

这道题还是挺有意思的, 刚开始想了一个$dp$的做法, 用树套树优化, 但是数据范围太大了, 很难卡过.

实际上可以直接考虑最终确定的区间$[L,R]$, 那么一个工人$(L_i,v_i,R_i)$会对所有$L_i\le L\le v_i,v_i\le R\le R_i$的区间产生贡献. 这样就转化为二维区间加, 最终查询所有点最值, 可以用线段树扫描线很容易实现.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head




const int N = 1e6+10;
int n, cnt, mx;
struct {
    int l,v,r;
} e[N];
struct _ {
    int l,r,h,v;
} a[N];
struct node {
    int mx,tag,pos;
    void upd(int x) {mx+=x,tag+=x;}
    node operator + (const node &rhs) const {
        node ret;
        ret.mx = max(mx,rhs.mx);
        ret.pos = ret.mx==mx?pos:rhs.pos;
        ret.tag = 0;
        return ret;
    }
} tr[N<<2];

void pd(int o) {
    if (tr[o].tag) {
        tr[lc].upd(tr[o].tag);
        tr[rc].upd(tr[o].tag);
        tr[o].tag=0;
    }
}
void add(int o, int l, int r, int ql, int qr, int v) {
    if (ql<=l&&r<=qr) return tr[o].upd(v);
    pd(o);
    if (mid>=ql) add(ls,ql,qr,v);
    if (mid<qr) add(rs,ql,qr,v);
    tr[o] = tr[lc]+tr[rc];
}
void build(int o, int l, int r) {
    tr[o].mx=tr[o].tag=0,tr[o].pos=l;
    if (l!=r) build(ls),build(rs);
}

int main() {
    cin>>n;
    REP(i,1,n) { 
        cin>>e[i].l>>e[i].v>>e[i].r;
        a[++cnt] = {e[i].l,e[i].v,e[i].v,1};
        a[++cnt] = {e[i].l,e[i].v,e[i].r+1,-1};
        mx = max(mx, e[i].r+1);
    }
    sort(a+1,a+1+cnt,[](_ a,_ b){return a.h<b.h;});
    build(1,1,mx);
    int now = 1, ans = 0;
    pii pos;
    REP(i,1,mx) {
        while (now<=cnt&&a[now].h<=i) add(1,1,mx,a[now].l,a[now].r,a[now].v),++now;
        if (tr[1].mx>ans) {
            ans = tr[1].mx;
            pos = pii(tr[1].pos,i);
        }
    }
    printf("%d\n", ans);
    REP(i,1,n) {
        if (e[i].l<=pos.x&&pos.x<=e[i].v&&e[i].v<=pos.y&&pos.y<=e[i].r) {
            printf("%d ",i);
        }
    }
    puts("");
}
View Code

377E Cookie Clicker

大意: $n$种建筑, 第$i$个花费$c_i$个曲奇, 每秒生产$v_i$曲奇. 每秒钟只能选一个建筑工作. 初始时刻$0$, 曲奇数$0$, 若$t$时刻选择一个建筑工作, 那么$t+1$时刻得到收益. 求得到$s$块曲奇最少用时.

斜率优化dp

include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head



const int N = 1e6+10;
int n;
ll s, dp[N];
struct _ {
    ll c, v;
    bool operator < (const _ &rhs) const {
        return c<rhs.c||c==rhs.c&&v<rhs.v;
    }
} a[N];

ll slope2(int i, int j) {
    return (a[j].v-a[i].v-1+dp[i]-dp[j])/(a[j].v-a[i].v);
}
ll chk(int i, int j, int k) {
    return slope2(i,j)>=slope2(j,k);
}
ll slope(int i, int j) {
    return (dp[i]-dp[j])/(a[j].v-a[i].v);
}

int main() {
    cin>>n>>s;
    REP(i,1,n) cin>>a[i].v>>a[i].c;
    sort(a+1,a+1+n);
    memset(dp,INF,sizeof dp);    
    deque<int> q;
    ll ans = 1e18;
    REP(i,1,n) {
        if (q.size()&&a[q.back()].v>=a[i].v) continue;
        while (q.size()>1&&slope(q[0],q[1])*a[q[0]].v+dp[q[0]]<a[i].c) q.pop_front();
        if (i==1) dp[i] = 0;
        else if (q.size()) {
            ll t = (a[i].c-dp[q[0]]+a[q[0]].v-1)/a[q[0]].v;
            ll rem = t*a[q[0]].v+dp[q[0]]-a[i].c;
            dp[i] = rem-t*a[i].v;
        }
        else continue;
        ans = min(ans, (s-dp[i]+a[i].v-1)/a[i].v);
        while (q.size()>1&&chk(q[q.size()-2],q.back(),i)) q.pop_back();
        q.push_back(i);
    }
    printf("%lld\n", ans);
}
View Code

 

转载于:https://www.cnblogs.com/uid001/p/11298911.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值