CF1070

CF1070

Eric 共同处理的一套题,这里处理奇数题号。

A

Description

给定两个数 \(d(1\le d \le 500)\)\(s(1\le s\le 5000)\),找出最小数 \(n\) 使得 \(d\mid n\)\(n\) 的各个位数之和为 \(s\)

Solution

数字很小,直接设状态 \((x,y)\) 表示和为 \(x\) 余数 \(y\) 是否出现过,记录一下转移的前缀。我们使用 BFS 确保最小。

Code

# include <bits/stdc++.h>

using namespace std;

# define PII pair< int , int >
# define MP make_pair
# define fi first
# define se second

const int D = 500 + 5 , S = 5000 + 5;
int s , d , vis[S][D];
queue< PII > Q;
pair< PII , int > f[S][D];

void Prt(int x , int y)
{
    if(!x && !y) return ;
    Prt(f[x][y].fi.fi , f[x][y].fi.se);
    cout << f[x][y].se;
}

int main()
{
    cin >> d >> s;
    Q.push(MP(0 , 0));
    vis[0][0] = 1;
    while(!Q.empty())
    {
        int x = Q.front().fi , y = Q.front().se;
        Q.pop();
        for(int i = 0; i <= 9; i++)
        {
            int nx = x + i , ny = (y * 10 + i) % d;
            if(nx <= s && !vis[nx][ny])
            {
                vis[nx][ny] = 1;
                Q.push(MP(nx , ny));
                f[nx][ny] = MP(MP(x , y) , i);
            }
        }
    }
    if(!vis[s][0]) cout << -1;
    else Prt(s , 0);
    return 0;
}
  • 一开始从低位到高位扩展,无法处理连续 \(0\) 的数量,数位 DP 根本不熟啊。

C

Description

\(\text{Buber}\) 是一家致力于浪费投资者的钱的 \(\text{Berland}\) 技术公司。最近,\(\text{Buber}\) 决定将他的设施转移到云端。公司决定连续 \(n\) 天云端租用 \(\text{CPU}\)\(\text{Buber}\)每天都要租用 \(k\)\(\text{CPU}\)

云端供应商提供 \(m\)个租用计划,第\(i\)个计划有如下的特征:

  • \(l_i,r_i\) 表示第 \(i\) 个计划从第 \(l_i\) 天开始,第 \(r_i\) 天结束。
  • \(c_i\) 表示第 \(i\) 个计划中,每天最多租用\(\text{CPU}\)个数。
  • \(p_i\) 表示第 \(i\) 个计划中,租用一个\(\text{CPU}\)一天的花费。

\(\text{Buber}\) 可以同时使用多个计划,即他可以在第 \(x\) 天在每个进行中的计划(即\(l_i≤x≤r_i\))中租用 \([0,c_i]\)\(\text{CPU}\).

现在 \(\text{Buber}\) 告诉你正整数 \(k\) ,表示每天所需的 \(\text{CPU}\) 个数(如果某一天无论怎么租用都不能租到 \(k\) 个,他也希望租尽量多的\(\text{CPU}\))。请你告诉他,他在这 \(n\) 天中最少需要花费多少钱来租用 \(\text{CPU}\)

Solution

很简单,之间时间扫描线加一个权值线段树就做完了。

但是,为了做的更加复杂,学习了一下树状数组倍增,一样的道理,单点加,区间查,多余的部分用 set 存一下就好了。

Code

# include <bits/stdc++.h>

using namespace std;

# define int long long

int read()
{
# define C ch = getchar()
    int x = 0; char C;
    for(; ch > '9' || ch < '0'; C); for(; ch >= '0' && ch <= '9'; C) x = (x << 3) + (x << 1) + (ch ^ 48);
    return x;
}

const int N = 1e6 + 5;
int n , k , m , ans;
set< int > s;

struct node
{
    int val , num;
    node() {}
    node(int val , int num) : val(val) , num(num) {}
} ;
vector< node > vec[N];

struct BIT
{
    int s1[N] , s2[N];
    int Lowbit(int x) {return x & -x;}
    void Upt(int x , int d1 , int d2) {for(; x < N; x += Lowbit(x)) s1[x] += d1 , s2[x] += d2;}
    int Query1(int x) {int res = 0; for(; x; x -= Lowbit(x)) res += s1[x]; return res;}
    int Query2(int x) {int res = 0; for(; x; x -= Lowbit(x)) res += s2[x]; return res;}
} bit;

signed main()
{
    n = read() , k = read() , m = read();
    for(int i = 1 , l , r , c , p; i <= m; i++)
    {
        l = read() , r = read() , c = read() , p = read();
        vec[l].emplace_back(p , c);
        vec[r + 1].emplace_back(p , -c);
    }
    for(int i = 1 , p , c; i <= n; i++)
    {
        for(auto x : vec[i])
        {
            p = x.val , c = x.num;
            bit.Upt(p , c , p * c);
            if(bit.Query1(p) - bit.Query1(p - 1)) s.insert(p);
            else if(s.find(p) != s.end()) s.erase(p);
        }
        if(bit.Query1(N - 1) <= k) {ans += bit.Query2(N - 1); continue ;}
        p = 0 , c = 0;
        for(int j = 20; ~j; j--)
        {
            int np = p | (1 << j);
            if(np > N - 1) continue ;
            int nc = c + bit.s1[np];
            if(nc > k) continue ;
            p = np , c = nc;
        }
        ans += bit.Query2(p);
        if(c == k) continue ;
        p = *s.upper_bound(p);
        ans += p * (k - c);
    }
    cout << ans;
    return 0;
}
  • 没什么难度,时间花在树状数组倍增上了。

E

Description

Polycarp有很多工作要做。最近他学会了一条新的时间管理技巧:“如果任务需要五分钟或更短时间,请立即执行”。Polycarp喜欢新技巧,但他不确定五分钟是最佳值。他认为这个值 d(分钟)应根据现有任务列表选择。

Polycarp有一份长度为 n 的要完成的任务清单。第 i 个任务有一个需要的时间Pi,即它确切地需要 Pi 分钟来完成。Polycarp从第一个到第 n 个逐个浏览任务。如果任务所需时间是 d 或更少,Polycarp立即开始执行任务。如果任务需要时间大于 d ,他不会去完成这个任务。列表中的任务的顺序是固定的了,所以,不容许你重新排序。当Polycarp阅读任务或跳过任务时,Polycarp不会花任何时间。

Polycarp有 T 分钟完成任务。但他不想一直工作。他决定在每做 m 个任务后休息一下。每次休息时间应该与完成这 m 个任务的时间相同。

Solution

是一个单峰函数,显然可以三分,但是我的三分一直是挂的。

考虑函数的性质,发现峰值一定是在一个因为时间超掉和任务无法做的临界值,那么我们就可以二分去找了。

Code

# include <bits/stdc++.h>

using namespace std;

# define int long long

int read()
{
# define C ch = getchar()
    int x = 0; char C;
    for(; ch > '9' || ch < '0'; C); for(; ch >= '0' && ch <= '9'; C) x = (x << 3) + (x << 1) + (ch ^ 48);
    return x;
}

const int N = 2e5 + 5;
int n , m , t , p[N] , h[N] , cnt;

int check(int x)
{
    cnt = 0;
    int sum = 0 , res = 0 , tot = 0;
    for(int i = 1; i <= n; i++)
    {
        if(p[i] > x) continue ;
        if(sum + p[i] > t) return 0;
        cnt++ , sum += p[i] , res += p[i] , tot++;
        if(tot == m) sum += res , res = 0 , tot = 0;
    }
    return 1;
}

void solve()
{
    n = read() , m = read() , t = read();
    for(int i = 1; i <= n; i++) h[i] = p[i] = read();
    sort(h + 1 , h + n + 1);
    int _n = unique(h + 1 , h + n + 1) - h - 1;
    int l = 1 , r = _n;
    while(l < r)
    {
        // cout << l << ' ' << r << endl;
        int mid = l + r + 1 >> 1;
        if(check(h[mid])) l = mid;
        else r = mid - 1;
    }
    check(h[l]);
    // cout << '*';
    int a0 = cnt , a1 = 0;      
    if(l < _n) check(h[l + 1]) , a1 = cnt;
    if(!a0 && !a1) cout << "0 1\n";
    else if(a0 < a1) cout << a1 << ' ' << h[l + 1] << '\n';
    else cout << a0 << ' ' << h[l] << '\n';
}

signed main()
{
    int T = read();
    while(T--) solve();
    return 0;
}

I

Descriptioin

给一个 \(n \leq 600\) 个点 \(m \leq 600\) 条边的无向图和一个参数 \(k\),现在需要对图上的所有边染色,满足:

  • 所有颜色为 \(1\)\(100500\) 间的整数;
  • 同种颜色的边最多两条;
  • 每个点周围所有边的不同颜色数 \(\le k\)

构造染色方案,若无解输出 \(m\)\(0\)

Solution

以为是什么神奇构造,其实是个网络流。

现在我们知道这个是网络流,那么我们反过来想想有什么线索告诉我们这个是网络流呢?首先这个数据范围就很网络流,之后是限制条件,同种颜色的边最多两条,限制很紧,单范围不大。有点有边的情况我们考虑 \(n\) 个点表示点 \(m\) 个点表示边。

我们记点的度数为 \(d\),一个点的所有边中有 \(a\) 对相同颜色的边,那么 \(d-a\leq k\),我们就能够得到 \(a\) 的最小值 \(d-k\)(这里就有一个判无解的地方了)。

之后怎么办?考虑一条边,只能给一个端点对于 \(a\) 一点贡献,因为至多两条同种颜色边,一对相同颜色边只可能在一个点里。

之后就好处理了,连边跑最大流,通过流量是否为 \(0\) 判断给了哪个点贡献。

Code

# include <bits/stdc++.h>

using namespace std;

int read()
{
# define C ch = getchar()
    int x = 0; char C;
    for(; ch > '9' || ch < '0'; C); for(; ch >= '0' && ch <= '9'; C) x = (x << 3) + (x << 1) + (ch ^ 48);
    return x; 
}

const int N = 1e5 , INF = 0x3f3f3f3f;
int n , m , k , deg[N] , sum , col[N] , cnt;
int ecnt = 1 , head[N] , nxt[N] , to[N] , w[N];
vector< int > e[N];

void AddEdge(int u , int v , int w)
{
    nxt[++ecnt] = head[u] , head[u] = ecnt , to[ecnt] = v , ::w[ecnt] = w;
    nxt[++ecnt] = head[v] , head[v] = ecnt , to[ecnt] = u , ::w[ecnt] = 0;
}

int s , t , lev[N] , cur[N];

bool BFS()
{
    for(int i = s; i <= t; i++) lev[i] = -1 , cur[i] = head[i];
    lev[s] = 0;
    queue< int > Q;
    Q.push(s);
    while(!Q.empty())
    {
        int u = Q.front(); Q.pop();
        for(int i = head[u]; i; i = nxt[i])
        {
            int v = to[i];
            if(w[i] && lev[v] == -1)
            {
                lev[v] = lev[u] + 1 , Q.push(v);
                if(v == t) return 1;
            }
        }
    }
    return 0;
}

int Dinic(int u , int flow)
{
    if(u == t) return flow;
    int res = 0 , delta;
    for(int &i = cur[u]; i; i = nxt[i])
    {
        int v = to[i];
        if(lev[v] > lev[u] && w[i])
        {
            delta = Dinic(v , min(flow - res , w[i]));
            if(delta)
            {
                w[i] -= delta;
                w[i ^ 1] += delta;
                res += delta;
                if(res == flow) break ;
            }
        }
    }
    if(res != flow) lev[u] = -1;
    return res;
}

int MaxFlow()
{
    int res = 0;
    while(BFS()) res += Dinic(s , INF);
    return res;
}

void solve()
{
    n = read() , m = read() , k = read();
    ecnt = 1;
    cnt = sum = 0;
    s = 0 , t = n + m + 1;
    for(int i = s; i <= t; i++) deg[i] = head[i] = col[i] = 0 , e[i].clear();
    for(int i = 1 , u , v; i <= m; i++)
    {
        u = read() , v = read();
        deg[u]++ , deg[v]++;
        AddEdge(s , i , 1) , AddEdge(i , u + m , 1) , AddEdge(i , v + m , 1);
    }
    for(int i = 1; i <= n; i++)
    {
        if(deg[i] > k * 2)
        {
            for(int i = 1; i <= m; i++) cout << 0 << ' '; cout << '\n';
            return ;
        }
        int out = max(0 , deg[i] - k);
        AddEdge(i + m , t , out * 2);
        sum += out * 2;
    }
    int ans = MaxFlow();
    if(ans != sum)
    {
        for(int i = 1; i <= m; i++) cout << 0 << ' '; cout << '\n';
        return ;
    }
    for(int i = 1; i <= m; i++)
    {
        if(!w[i * 6 - 2]) e[to[i * 6 - 2] - m].push_back(i);
        else if(!w[i * 6]) e[to[i * 6] - m].push_back(i);
    }
    for(int i = 1; i <= n; i++)
        for(int j = 0; j < e[i].size(); j += 2)
            col[e[i][j]] = col[e[i][j + 1]] = ++cnt;
    for(int i = 1; i <= m; i++)
    {
        if(!col[i]) col[i] = ++cnt;
        cout << col[i] << ' ';
    }
    cout << '\n';
}

int main()
{
    int T = read();
    while(T--) solve();
    return 0;
}
  • 笑死,根本想不到网络流。

讲个笑话:

lev[v] == lev[u] + 1

调了 \(40\) 分钟。

posted @ 2023-12-25 14:01  Terdy  阅读(41)  评论(0)    收藏  举报