Educational Codeforces Round 171 (Rated for Div. 2)

A. Perpendicular Segments

分析

题目中的要求34,说明需要较短的线段尽量长,那么两个线段应该一样长

而又要求线段垂直, 那么两线段可以放在一个正方形内做对角线

那么此时xy对称(代数意义上?), 取两个的较小值做一个正方形,答案即为对角线

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
const int N=1e6+10;
const int mod=998244353;
const int INF  = 0x3f3f3f3f;
const ll INFll  = 0x3f3f3f3f3f3f3f3f;
#define endl "\n" 

//vector<vector<int>>adj(N);


void solve()
{
    int x, y, k;
    cin >> x >> y >> k;
    if(x > y) swap(x, y);
    cout << "0 0 " << x << " " << x << endl;
    cout << "0 " << x << " " << x << " 0" << endl;
}


signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cout << setprecision(11) << fixed;
    int t;t=1;
    cin>>t;
    for(int i=1;i<=t;i++){
        //printf("Case %d: ",i);
        solve();
    }
}

B. Black Cells

赛时脑瘫了,看到1e18不敢二分,硬写了个o(n), 成功wa2

分析

直接二分k的大小
分析check
如果n是偶数,最好的情况就是每次选相邻的两个,这种情况甚至不需要二分,直接算出来

否则可以选一个不染,当发现此时k不够染相邻2个时,舍掉左边那个,继续染剩下的

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
const int N=1e6+10;
const int mod=998244353;
const int INF  = 0x3f3f3f3f;
const ll INFll  = 0x3f3f3f3f3f3f3f3f;
#define endl "\n" 

//vector<vector<int>>adj(N);

int a[N];
int b[N],c[N];
int n;
bool check(int mid) {
    for(int i = 1; i + 1 <= n; i += 2) {
        if(a[i + 1] - a[i] > mid) {
            if(n % 2 == 0) return false;
            if(i % 2 == 0) return false;
            i --;
        }
    }
    return true;
} 


void solve()
{
   
    cin >> n;
    for(int i = 1; i <= n; i ++) {
        cin >> a[i];
    }
    int l = 1, r = 1e18;
    while(l < r) {
        int mid = l + r >> 1;
        if(check(mid)) r = mid;
        else l = mid + 1;
    }

    cout << l << endl;
}


signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cout << setprecision(11) << fixed;
    int t;t=1;
    cin>>t;
    for(int i=1;i<=t;i++){
        //printf("Case %d: ",i);
        solve();
    }
}

C. Action Figures

分析

首先,在某天买东西时,一定会试图免费第i个物品,因为这是可买中,最贵的

所以,当s[i]==0 时, i物品的钱一定要出。

所以我们考虑那些物品可以被免费

逆向枚举每一天,同时,用一个队列来存需要免费的物品

  • 如果s[i]==1, 丢到队列里面
  • 如果s[i]==0, 记录价值,同时取出队首(最贵的)
    当枚举后,队列可能不空,需要支付里面较小的一半的钱,同时也可以免费较大的
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
const int N=1e6+10;
const int mod=998244353;
const int INF  = 0x3f3f3f3f;
const ll INFll  = 0x3f3f3f3f3f3f3f3f;
#define endl "\n" 

//vector<vector<int>>adj(N);


void solve()
{
    int n; cin >> n;
    string s; cin >> s;
    s = " " + s;
    int cnt = 0;
    queue<int> q;
    int ans = 0;
    vector<int>a(n + 1);

    for(int i = n; i >= 1; i --) {
        if(s[i] == '1') q.push(i);
        else {
            ans += i;
            if(q.size())q.pop();
        }
    } 

    int t = (q.size() + 1)/2; // 这里没明白可以改为双端队列,一个一个慢慢算
    while(q.size()) {
        if(q.size() <= t) ans += q.front();
        q.pop();
    }
    cout << ans << endl;

}


signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cout << setprecision(11) << fixed;
    int t;t=1;
    cin>>t;
    for(int i=1;i<=t;i++){
        //printf("Case %d: ",i);
        solve();
    }
}

D. Sums of Segments

写的最屎的一题,正在试图看懂赛时代码

分析

将题目的 b 数组重新排列成如下矩阵,成为 c 矩阵

举个例子,对于 s(i,j) 这个位置在 b 数组中的前缀和,来源两部分

  • i1 行所有元素的和
  • i[i,j] 的和

第一步,计算 b 中某个元素在 c 矩阵的位置

  • 这个挺好算的,计算 c 矩阵每一个元素的个数,做前缀和然后二分一下在多少行,减去前面的总数,就知道在当前行的第几个,参考代码中 d 数组的使用

第二步, 计算部分1

  • 参考代码 s22 数组

第三步, 计算部分2

  • 对于第 i[i,j] 的和,观察 c 矩阵, c[i][j]=c[i1][j]a[i];
  • 赛时写到这里想掏线段树了,但好在不带修。计算这个区间在第一行的和,对于第i行的每个元素,都多算了一次是 s[i1] ,剪掉就好

把需要的东西都预处理好了,可以 log 求出 b 数组的某个前缀和,然后做个差就是区间和

参考代码一起看


#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
const int N=1e6+10;
const int mod=998244353;
const int INF  = 0x3f3f3f3f;
const ll INFll  = 0x3f3f3f3f3f3f3f3f;
#define endl "\n" 

//vector<vector<int>>adj(N);


void solve()
{
    int n; cin >> n;
    vector<int> a(n + 1, 0);
    vector<int> s(n + 1, 0);
    vector<int> b(n + 1, 0); // b数组的前n个元素,
    vector<int> s2(n + 1, 0); // c矩阵中每一行的和
    vector<int> s22(n + 1, 0);// s2的前缀和,用于快速计算c矩阵前i行的的和
    vector<int> d(n + 1, 0); // c矩阵的每一行有效元素数量的前缀和,用来确定当前数在多少行

    for(int i = 1; i <= n; i ++) {
        cin >> a[i];
        s[i] = a[i] + s[i - 1];
        b[i] = b[i - 1] + s[i];
        d[i] = n - i + 1 + d[i - 1];
    }

    s22[1] = s2[1] = b[n];
    for(int i = 2; i <= n; i ++) {
        s2[i] = s2[i - 1] - (n - i + 2) * a[i - 1];
        s22[i] = s2[i] + s22[i - 1];
    }

    auto get = [&](int l, int r) -> int { // 计算c矩阵中,从(l,l)到(l, l + r - 1)的和
        return b[r] - b[l - 1] - (s[l - 1]) * (r - l + 1);
    };

    auto calc = [&](int x) -> int {
        int i = lower_bound(d.begin() + 1, d.end(), x) - d.begin();
        int res = s22[i - 1];
        x -= d[i - 1];
        res += get(i, i + x - 1);
        return res;
    };


    int q; cin >> q;
    while(q --) {
        int x, y; cin >> x >> y;
        cout << calc(y) - calc(x - 1) << endl;
    }
}


signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cout << setprecision(11) << fixed;
    int t;t=1;
    //cin>>t;
    for(int i=1;i<=t;i++){
        //printf("Case %d: ",i);
        solve();
    }
}

E. Best Subsequence

听说是最小权闭合子图,但赛时写到这只有十几分钟了,而且根本没看出来是网络流

分析

很板子的一个题目,最难的地方就是看出他是板子

如果选择了ai,那么获利就是1,但是要选择 ai 中所有二进制为1的位置,这个就是花费
可以参考我代码里面的注释

hack时间还没结束,满猜有人每次memset(h, -1, sizeof h)会被卡

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
const int N=200+10,M = 2 * N * N;
const int mod=998244353;
const int INF  = 0x3f3f3f3f3f3f3f3f;
const ll INFll  = 0x3f3f3f3f3f3f3f3f;
#define endl "\n" 


/*
使用说明
需要识别出闭合子图,
所谓闭合子图就是给定一个有向图,从中选择一些点组成一个点集V。对于V中任意一个点,其后续节点都仍然在V中
闭合子图(选择出点集,没有出去的有向边)
选择一个闭合子图, 这个权是最大的

举例    
制造一些物品,可以获得对应的利益,
同时在制作的过程中,需要花费一些代价去买需要的东西
那么物品到对应的所需物品就有一个边,如果我选择了这个物品,那就一定要选择所需物品,
这不仅仅是题目的要求,也是我选出的点集构成闭合子图的关键

注意点
1.所需物品应该是不会被消耗的,如果可以被消耗,那么直接将所需花费从获利中剪掉就行 
    (?)如果物品有消耗次数,制作某个物品只会被消耗一部分,没碰见过,不知道
2.所需物品是多个制作品可以共用的,(可以 != 必须)
    只服务单次制作的话,直接剪掉就好,理由同上


做法
转化为最小割,用最大流求解

1.原本所有的依赖关系全部保留,但是容量是INF
2.所有的制作获利, 将他们从S连边,容量是 本次获利
3.所有的依赖品 , 全部连向T, 容量是 abs(消耗)
从源点s向每个正权点连一条容量为权值的边,每个负权点向汇点t连一条容量为权值的绝对值的边,有向图原来的边容量全部为无限大。

跑完后,ans = sum(正权) - 最小割

最优性
最小割=(不选的正权之和+要选的负权绝对值之和)
最大权闭合子图=(正权之和-不选的正权之和-要选的负权绝对值之和)=正权值和-最小割
因为正权值和,是定值,而最小割保证值最小,所以最大权闭合子图一定最优。

详细解释链接
https://blog.csdn.net/can919/article/details/77603353


*/

//vector<vector<int>>edg(N);
int n, m, S, T;
ll h[N], e[M], ne[M], f[M], idx;
int d[N], cur[N];


void add(int a, int b, int c) {
    // cout << a << " " << b << " " << c << endl;
    e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++;
    e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++;
}

bool bfs() {
    memset(d, -1, sizeof d);
    queue<int> q;
    q.push(S), d[S] = 0, cur[S] = h[S];

    while(q.size()) {
        int u = q.front(); q.pop();
        for(int i = h[u]; ~i; i = ne[i]) {
            int v = e[i];
            if(d[v] == -1 && f[i]) {
                d[v] = d[u] + 1;
                cur[v] = h[v];
                if(v == T) return true;
                q.push(v);
            }
        }
    }
    return false;
}

int find(int u, ll limit) {
    if(u == T) return limit;
    ll flow = 0;

    for(int i = cur[u]; ~i && flow < limit; i = ne[i]) {
        int v = e[i];
        cur[u] = i;
        if(d[v] == d[u] + 1 && f[i]) {
            int t = find(v, min(f[i], limit - flow));
            if(!t) cur[u] = -1;
            f[i] -= t, f[i ^ 1] += t, flow += t;
        }
    }
    return flow;
}

ll dinic() {
    ll ans = 0, flow;
    while(bfs())
        if(flow = find(S, INF))
            ans += flow;
    return ans;
}

int id64[100], aid[N], id;
int a[N];

void clear() {
    memset(h, -1, sizeof h);
    id = 0;
    idx = 0;
}


void solve()
{
    int n;
    cin >> n;
    S = id++, T = id++;
    for(int i = 0; i < 63; i ++) {
        id64[i] = id++;
        add(id64[i], T, 1);
    }

    for(int i = 1; i <= n; i ++) {
        cin >> a[i];
        aid[i] = id ++;
        for(int j = 0; j < 63; j ++) {
            if((a[i] >> j) & 1) add(aid[i], id64[j], INF);
        }
        add(S, aid[i], 1);
    }

    cout <<  n - dinic() << endl;
}


signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cout << setprecision(11) << fixed;
    int t;t=1;
    cin>>t;
    for(int i=1;i<=t;i++){
        clear();  
        //printf("Case %d: ",i);
        solve();
    }
}
posted @   Haborym  阅读(121)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示