AtCoder Beginner Contest 245

AtCoder Beginner Contest 245

C

题意

两个序列 \(a,b\) ,每次从中选择一个元素 \(x_i\) ,构成一个新序列。

问新序列是否有满足对任意的 \(i\) ,满足 \(| x_{i +1} - x_i| <= K\)

思路

如果可以知道前 \(i\) 个元素可以达成的最优解, 前 $i + 1 $ 个元素的最优解就之后当前选择元素和相邻元素差和前状态有关。

因此考虑 \(dp\)

定义 \(f[i][0/1]\) 表示考虑长 \(i\) 的前缀,最后一个选第 \(0/1\) 个序列中元素所能取得的最小相邻差值

转移只需分前一次决策选第一个还是第二个序列中元素讨论即可

\[f[i][0/1] = min(f[i][0/1],max(f[i - 1][j],abs(a[i] - (j ? b[i - 1] : a[i - 1])))) \]

#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout<<'>' << ' ' << x<<endl;
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int N,K;
    cin >> N >> K;
    vector<int> a(N + 1),b(N + 1);
    for(int i = 1;i <= N;i ++) cin >> a[i];
    for(int i = 1;i <= N;i ++) cin >> b[i];

    vector<vector<int>> f(N + 1,vector<int>(2,linf));
    
    f[1][0] = f[1][1] = 0;  
    for(int i = 2;i <= N;i ++) {
        for(int j = 0;j < 2;j ++) {
            f[i][0] = min(f[i][0],max(f[i - 1][j],abs(a[i] - (j ? b[i - 1] : a[i - 1]))));
            f[i][1] = min(f[i][1],max(f[i - 1][j],abs(b[i] - (j ? b[i - 1] : a[i - 1]))));
        }
    }

    if(min(f[N][0],f[N][1]) > K) cout << "No\n";
    else cout << "Yes\n";
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

    //int T;cin>>T;
    //while(T--)
        solve();

    return 0;
}

D

题意

\(n\) 次多项式 \(A_n\) ,和 \(m\) 次多项式 \(B_m\)

它们的乘积为 $C_{n + m} = A_n * B_m $

现在给你 \(A_n\)\(C_{n + m}\) 的各次系数。

\(B_m\) 各次系数

思路

先模拟任意 \(C_{n + m}\) 的系数 \(c_k\) ,可以发现:

\[c_i = \sum_{j = 0}^{i} a_{i - j} * b_j (i - j >= 0) \]

此时我们有了 \(n + m\) 个方程,现在考虑如何方便的枚举求解方程

因为题中给出 \(a_n \neq 0\) ,所以可以把含 \(a_n * b_j\) 的方程拿出来,变形后有

\[b_j = (c_{n + j} - \sum_{k = 0}^{n - 1} a_k * b_{n + j - k}) \div a_n \]

以此计算即可,

#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout<<'>' << ' ' << x<<endl;
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int N,M;
    cin >> N >> M;
    vector<int> a(N + 1),b(M + 1),c(1 + N + M);
    for(int i = 0;i <= N;i ++) cin >> a[i];
    for(int i = 0;i <= M + N;i ++) cin >> c[i];

    b[M] = c[N + M] / a[N];
    for(int i = N + M - 1, j = M; i >= N; i -- ,j --) {

    for(int k = 0; k <= N; k ++ ) {
        c[j + k] -= a[k] * b[j];
    }
    b[j - 1] = c[i] / a[N];
  }
    for(int i = 0;i <= M;i ++) cout << b[i] << " \n"[i == M];
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

    //int T;cin>>T;
    //while(T--)
        solve();

    return 0;
}

E

题意

\(N\) 个矩形巧克力放 \(M\) 个盒子

只要巧克力的长宽都小于等于盒子时才可以放

每个盒子只能放一个

问是否可以把 \(N\) 个全放盒子里

思路

因为有两维属性,直接排序贪心肯定不行(比如 (2,100),(3,5)放(3,5),(4,200)就是一组反例)

重新考虑这个问题

实际上就是在做先从第一位属性满足要求的盒子中选择第二维属性也合适的盒子这样一件事。

第一维可以暴力枚举,现在要做的是在选出的盒子集合中快速找到第二维中满足要求的盒子。

这个过程可以用二分加速。

另外枚举时应该从最难放进盒子里巧克力开始枚举。

因为我们维护的是长度满足要求的集合,如果从小到大,无法保证之后的巧克力的长度比这个集合中的盒子小

#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout<<'>' << ' ' << x<<endl;
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int N,M;
    cin >> N >> M;
    vector<PII>a(N),c(M);
    for(int i = 0;i < N;i ++) cin >> a[i].first;
    for(int i = 0;i < N;i ++) cin >> a[i].second;
    for(int i = 0;i < M;i ++) cin >> c[i].first;
    for(int i = 0;i < M;i ++) cin >> c[i].second;
    multiset<int> s;
    sort(a.begin(),a.end(),greater<PII>());
    sort(c.begin(),c.end(),greater<PII>());

    int used = 0;
    for(int i = 0;i < N;i ++) {
        while(used < M && c[used].first >= a[i].first) {
            s.insert(c[used].second);
            used ++;
        }
        auto it = s.lower_bound(a[i].second);
        if(it == s.end()) {
            cout << "No\n";
            return;
        }
        s.erase(it);
    }

    cout << "Yes\n";
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

    //int T;cin>>T;
    //while(T--)
        solve();

    return 0;
}

F

题意

给出一个有向图,如果从某一点出发,可以无限走下去,答案加一

求最后有多少个点满足这样的条件

思路

显然就是找有多少个点可以走到环上。

首先环上的点一定可行,先把环当一个 超级点 考虑。

此时就是一个有向无环图了

某点如果可达超级点,那么它也是可行的。

所以一个比较麻烦的解法就是 缩点 + 搜索,当搜到超级点,终止操作。

但如果想到 环的入度至少为一 这个性质。

如果用 拓扑排序 的方法统计可行点的个数,当到达一个环时刚好会终止。

但此时还有环上的点没有计数。

此时可以反向思考。

不合法的点就不涉及对环上点的统计

我们反向建边,将不合法的点统计出来就算出答案了。

#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout<<'>' << ' ' << x<<endl;
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int N,M;
    cin >> N >> M;
    vector<vector<int>> G(N + 1);
    vector<int> du(N + 1);
    for(int i = 0;i < M;i ++) {
        int u,v;
        cin >> u >> v;
        G[v].push_back(u);
        du[u] ++;
    }
    
    queue<int> q;
    for(int i = 1;i <= N;i ++) {
        if(!du[i]) {
            q.push(i);
        }
    }

    int ans = N;
    while(q.size()) {
        int u = q.front();
        q.pop();
        ans --;
        for(int v : G[u]) {
            if(-- du[v] == 0) {
                q.push(v);
            }
        }
    }

    cout << ans << endl;
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

    //int T;cin>>T;
    //while(T--)
        solve();

    return 0;
}
posted @ 2022-03-26 22:56  Mxrurush  阅读(65)  评论(0)    收藏  举报