2018 计蒜之道 初赛 第一场

15 篇文章 0 订阅
10 篇文章 0 订阅

A.百度无人车

题目大意:n个数,每将一个数减1,耗费p,问花费不超过s的情况下,最大值最小是多少

题解:都是套路,二分最大值就好了

#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define CLR(a) memset(a, 0, sizeof(a))
#define DBG(x) cout<<(#x)<<"="<<x<<endl
#define FOR(i, a, b)  for(int i=(a); i<(b); i++)
#define REP(i, a, b)  for(int i=(a); i<=(b); i++)
#define DOWN(i, a, b) for(int i=(a); i>=(b); i--)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1000000009;
const int N= 2e4 +10;

int n;
int a[N];
ll p, s;

bool solve(ll k) {
    ll ret=0;
    DOWN(i, n, 1) {
        if (a[i]<=k) break;
        if (a[i]>k) ret+=(a[i]-k)*p;
    }
    if (ret<=s) return true;
    else return false;
}

int main() {
    cin>>n;
    REP(i, 1, n) {
        cin>>a[i];
    }
    sort(a+1, a+n+1);
    cin>>p>>s;
    ll L=1, R=1e18;
    while(L<R) {
        ll M=L+(R-L)/2;
        if (solve(M)) R=M;
        else L=M+1;
    }
    cout<<L<<endl;
    //cout<<1.*clock()/CLOCKS_PER_SEC<<"ms"<<"\n";
    return 0;
}

B.百度科学家(简单)

题目大意:序列上有n个点,每个点有点权,两种操作,第一种操作,替换序列上一个x位置的点为一个新点,第二种操作,将序列上x上的点连序列l到r上的点。
点数个数<=20, 操作个数<=50

题解:由于点数和边数很小,直接暴力就好,枚举每个元素,注意这里不能用记忆化的dp,因为有可能是环。

#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define CLR(a) memset(a, 0, sizeof(a))
#define DBG(x) cout<<(#x)<<"="<<x<<endl
#define FOR(i, a, b)  for(int i=(a); i<(b); i++)
#define REP(i, a, b)  for(int i=(a); i<=(b); i++)
#define DOWN(i, a, b) for(int i=(a); i>=(b); i--)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1000000009;
const int N= 1e2 +10;

ll dp[N];
int cnt;
int x, y, l, r;
int g[N][N];
int n, m, id[N];
ll b[N]; 
int p[N];
int vis[N];
ll ans, tmp;

ll dfs(int cur) {
    vis[cur]=1;
    ll ret=b[cur];
    REP(i, 1, cnt) {
        if (i==cur) continue;
        if (!vis[i]&&g[cur][i]) {           
            ret+=dfs(i);
        }
    }
    return ret;
}

int main() {
    memset(dp, -1, sizeof(dp));
    cin>>n;
    REP(i, 1, n) {
        cin>>b[i];
        id[i]=i;    
    }
    cnt=n;
    cin>>m;
    REP(i, 1, m) {
        cin>>p[i];
        if (p[i]==0) {
            cin>>x>>y;
            ++cnt;
            b[cnt]=y;
            id[x]=cnt;
        }
        else if (p[i]==1) {
            cin>>x>>l>>r;
            REP(j, l, r) {
                g[id[x]][id[j]]=1;
            }
        }
    }
    ans=LL_INF;
    REP(i, 1, cnt) {
        memset(vis, 0, sizeof(vis));
        tmp=dfs(i);
        if (tmp<ans) ans=tmp;
        // DBG(tmp);
    }

    cout<<ans<<endl;
    //cout<<1.*clock()/CLOCKS_PER_SEC<<"ms"<<"\n";
    return 0;
}

/*
3
1 10 100
3
1 1 2 3
1 2 1 3
1 3 1 2
*/

B.百度科学家(中等)

题目大意:序列上有n个点,每个点有点权,两种操作,第一种操作,替换序列上一个x位置的点为一个新点,第二种操作,将序列上位置x上的点连序列上位置l到r上的点。
点数个数 1e5 ≤ 1 e 5 , 操作个数 1e5 ≤ 1 e 5 (rl+1)1e5 ∑ ( r − l + 1 ) ≤ 1 e 5

题解:由于点数正常,但边数较少,注意到每个强连通分量内的点,要么都选,要么都不选,所以tarjan缩点,枚举每个强连通分量,缩点后就变成DAG,直接上记忆化的dp就好了

#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define CLR(a) memset(a, 0, sizeof(a))
#define DBG(x) cout<<(#x)<<"="<<x<<endl
#define FOR(i, a, b)  for(int i=(a); i<(b); i++)
#define REP(i, a, b)  for(int i=(a); i<=(b); i++)
#define DOWN(i, a, b) for(int i=(a); i>=(b); i--)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1000000009;
const int N= 1e5 +10;

ll dp[N], sum[N];
int cnt;
int x, y, l, r;
int n, m, id[N], p;
ll b[N]; 
int vis[N];
ll ans, tmp;
vector<int> G[N];

vector<int> V[N];
int pre[N], lowlink[N], sccno[N], dfs_clock, scc_cnt;
stack<int> S;

void dfs(int u) {
    pre[u] = lowlink[u] = ++dfs_clock;
    S.push(u);
    FOR(i, 0, V[u].size()) {
        int v=V[u][i];

        if (!pre[v]) {
            //树边
            dfs(v);
            lowlink[u]=min(lowlink[u], lowlink[v]);
        }
        //非树边
        else if (!sccno[v]) {
            //非交叉边,即前向边
            lowlink[u] = min(lowlink[u], pre[v]);
        }
    }
    if (lowlink[u]==pre[u]) {
        scc_cnt++;
        while(1) {
            int x = S.top(); S.pop();
            sccno[x] = scc_cnt;
            if (x==u) break;
        }
    }
}

void find_scc(int n) {
    dfs_clock = scc_cnt = 0;
    memset(sccno, 0, sizeof(sccno));
    memset(pre, 0, sizeof(pre));
    for(int i=1; i<=n; i++) {
        if (!pre[i]) dfs(i);
    }
}


ll solve(int cur) {
    vis[cur]=1;
    if (dp[cur]>=0) return dp[cur];
    ll ret=sum[cur];
    FOR(i, 0, G[cur].size()) {
        int u=G[cur][i];
        if (vis[u]) continue;
        ret+=solve(u);
    }
    return dp[cur]=ret;
}

int main() {
    memset(dp, -1, sizeof(dp));
    cin>>n;
    REP(i, 1, n) {
        cin>>b[i];
        id[i]=i;    
    }
    cnt=n;
    cin>>m;
    REP(i, 1, m) {
        cin>>p;
        if (p==0) {
            cin>>x>>y;
            ++cnt;
            b[cnt]=y;
            id[x]=cnt;
        }
        else if (p==1) {
            cin>>x>>l>>r;
            REP(j, l, r) {
                if (x!=j) V[id[x]].pb(id[j]);
            }
        }
    }

    find_scc(cnt);
    REP(i, 1, cnt) {
        sum[sccno[i]]+=b[i];
    }
    for(int u=1; u<=cnt; u++) {
        for(int i=0; i<V[u].size(); i++) {
            int v=V[u][i];
            if (sccno[u]!=sccno[v]) {
                G[sccno[u]].pb(sccno[v]);
            }
        }
    }

    ans=LL_INF;
    REP(i, 1, scc_cnt) {
        memset(vis, 0, sizeof(vis));
        ll tmp = solve(i);
        if (tmp<ans) ans = tmp;
    }
    cout<<ans<<endl;
    //cout<<1.*clock()/CLOCKS_PER_SEC<<"ms"<<"\n";
    return 0;
}

/*
3
1 10 100
3
1 1 2 3
1 2 1 3
1 3 1 2
*/

B.百度科学家(中等)

题目大意:序列上有n个点,每个点有点权,两种操作,第一种操作,替换序列上一个x位置的点为一个新点,第二种操作,将序列上x上的点连序列l到r上的点。
总元素个数 1e5 ≤ 1 e 5 , 操作个数 1e5 ≤ 1 e 5 (rl+1)1e9 ∑ ( r − l + 1 ) ≤ 1 e 9

题解:由于点数正常,但边数爆炸,就需要数据结构优化了,待补。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值