Codeforces Round #595 (Div. 3)【AK】【并查集、数学、dp、差分】

41 篇文章 0 订阅
35 篇文章 0 订阅

Codeforces Round #595 (Div. 3)


A. Yet Another Dividing into Teams

  手速题

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
int N, a[305], b[305];
int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &N);
        memset(b, 0, sizeof(b));
        for(int i=1; i<=N; i++)
        {
            scanf("%d", &a[i]);
            b[a[i]]++;
        }
        int ans = 1;
        for(int i=2; i<=100; i++) ans = max(ans, min(b[i], b[i-1]) + 1);
        printf("%d\n", ans);
    }
    return 0;
}

 


B. Books Exchange 

  其实问的就是每个环内的点的个数,岂不是带权并查集维护一下就可以了嘛。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 2e5 + 7;
int N, root[maxN], siz[maxN];
inline int fid(int x) { return x == root[x] ? x : root[x] = fid(root[x]); }
inline void init()
{
    for(int i=1; i<=N; i++) { root[i] = i; siz[i] = 1; }
}
int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &N);
        init();
        for(int i=1, v; i<=N; i++)
        {
            scanf("%d", &v);
            int fv = fid(v), fu = fid(i);
            if(fv != fu)
            {
                root[fv] = fu;
                siz[fu] += siz[fv];
            }
        }
        for(int i=1; i<=N; i++) printf("%d ", siz[fid(i)]);
        printf("\n");
    }
    return 0;
}

 


C. Good Numbers

  一个被3^{x}的和表示的数是一个good number,但是,每个3^{x}不能超过一个。那么,我们可以去看二进制的表示,很容易发现,我们可以把一个数那三进制表示,但是有的位可能为2个,这时候就是去给前一位+1,然后对后面的所有直接赋值为0即可。

  直接贪心。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
ll x;
int bit[75], top;
inline void solve()
{
    top = 0;
    while(x)
    {
        bit[++top] = x % 3;
        x /= 3;
    }
}
int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        scanf("%lld", &x);
        memset(bit, 0, sizeof(bit));
        solve();
        if(bit[top] == 2)
        {
            top++;
            bit[top] = 1;
            for(int i=top-1; i>=0; i--) bit[i] = 0;
        }
        else
        {
            int i, j;
            for(i=top-1; i>=1; i--)
            {
                if(bit[i] == 2)
                {
                    bit[i+1]++;
                    for(j=i; j>=1; j--) bit[j] = 0;
                    break;
                }
            }
            for(int k=i+1; k<70; k++)
            {
                if(bit[k] >= 2)
                {
                    bit[k] = 0; bit[k+1]++;
                    if(k == top) top++;
                }
                else break;
            }
        }
        ll ans = 0, tmp = 1LL;
        for(int i=1; i<=top; i++)
        {
            if(bit[i]) ans += bit[i] * tmp;
            tmp = tmp * 3LL;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

 


D. Too Many Segments

  手玩一下,很容易发现,如果我们对左区间升序,然后去看右区间,这时候会发现,我们应该贪心的选择右端点最远到达的,并且左区间还得包含目前的这个点的,那么我们可以去搜索一下。

  现在,D2,N很大,这时候我们肯定得要去优化一下暴力的解法,这里就可以用一下差分来降维了。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 2e5 + 7;
int N, K;
struct node
{
    int r, id;
    node(int b=0, int c=0):r(b), id(c) {}
    friend bool operator < (node e1, node e2) { return e1.r < e2.r; }
};
vector<node> add[maxN], del[maxN];
//multiset<node> add[maxN], del[maxN];
multiset<node> now;
multiset<node>::iterator it;
vector<int> vt;
int main()
{
    scanf("%d%d", &N, &K);
    for(int i=1, l, r; i<=N; i++)
    {
        scanf("%d%d", &l, &r);
        add[l].push_back(node(r, i));
        del[r + 1].push_back(node(r, i));
    }
    for(int i=1; i<=200000; i++)
    {
        int len = (int)add[i].size();
        for(int j=0; j<len; j++) now.insert(add[i][j]);
        len = (int)del[i].size();
        for(int j=0; j<len; j++) now.erase(del[i][j]);
        len = (int)now.size();
        if(len > K)
        {
            int det = len - K;
            while(det)
            {
                it = now.end();
                vt.push_back((--it)->id);
                now.erase(it);
                det--;
            }
        }
    }
    int len = (int)vt.size();
    printf("%d\n", len);
    for(int i=0; i<len; i++) printf("%d ", vt[i]);
    printf("\n");
    return 0;
}

 


E. By Elevator or Stairs?

  DP,其实最短路也就可以了的。

二维DP,第二维的0表示直接走楼梯,第二维的1表示用了电梯,那么就是需要加上c了。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 2e5 + 7;
int N, C;
ll dp[maxN][2], a[maxN], b[maxN];
int main()
{
    scanf("%d%d", &N, &C);
    memset(dp, INF, sizeof(dp));
    dp[1][1] = C;
    dp[1][0] = 0;
    for(int i=1; i<N; i++) scanf("%lld", &a[i]);
    for(int i=1; i<N; i++) scanf("%lld", &b[i]);
    for(int i=2; i<=N; i++)
    {
        dp[i][0] = min(dp[i-1][0], dp[i-1][1]) + a[i-1];
        dp[i][1] = min(dp[i-1][0] + C, dp[i-1][1]) + b[i-1];
    }
    for(int i=1; i<=N; i++) printf("%lld ", min(dp[i][0], dp[i][1]));
    printf("\n");
    return 0;
}

 


F. Maximum Weight Subset

  树形DP(好像有人用一些什么最大权值的独立集给过了,什么操作啊喂!QAQ?)

  题目要找这样的集合,集合内的每个点对相互之间的距离都大于K,并且求其最大的权值。

  其实也就是去求最大权值独立集。

  怎么搞呢?我们可以去处理每个点的向下的间隔,譬如说,我们的间隔为0,是不是代表了我们把目前的这个点给取了,所以开一个二维dp[ i ][ j ],其中i表示的是结点的序号,j表示的是间隔,于是乎有dp[u][0] = a[u];

  画个图吧:

这时候,我们假设K为2,那么如果我取了1这个结点,是不是代表了是一定不可以取2、3两个结点的,然后以此类推。

  换个角度来看这个问题,我们现在假如要去取1号点,就代表了dp[1][0] = a[1]; 那么,我们还可以怎样的去表示1这号结点的贡献呢?dp[1][1] = dp[1][1] + max(dp[3][0], dp[7][0]),这个等式可以化简一下,dp[1][1] = dp[1][1] + max(dp[3][0], dp[3][1]),这里,“dp[3][1]”就是等同于“dp[7][0]”,因为代表了不去取3这个结点的a[3],而是改为取7号结点的a[7]。

  所以嘛,在这里,如果我们访问到根为u的子树,dp[u][i],以及子结点v,dp[v][j],如果“i + j + 1 > K”就是说明这是我们可以取的结点了,这里的j是需要“+1”的,是因为深度要往上提1了。然后这时候就是去更新答案了。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
int N, K, a[205], head[205], cnt, dp[205][205], ans[205];
struct Eddge
{
    int nex, to;
    Eddge(int a=-1, int b=0):nex(a), to(b) {}
}edge[505];
inline void addEddge(int u, int v)
{
    edge[cnt] = Eddge(head[u], v);
    head[u] = cnt++;
}
void dfs(int u, int fa)
{
    dp[u][0] = a[u];
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(v == fa) continue;
        dfs(v, u);
        memset(ans, 0, sizeof(ans));
        for(int x=0; x<=K+1; x++)
        {
            for(int y=0; y<=K+1; y++)
            {
                if(x + y >= K)
                {
                    ans[min(x, y + 1)] = max(ans[min(x, y + 1)], dp[u][x] + dp[v][y]);
                }
            }
        }
        for(int j=0; j<=K; j++) dp[u][j] = max(dp[u][j], ans[j]);
    }
    dp[u][0] = max(dp[u][0], dp[u][K + 1] + a[u]);
}
inline void init()
{
    for(int i=1; i<=N; i++) head[i] = -1;
    cnt = 0;
}
int main()
{
    scanf("%d%d", &N, &K);
    init();
    for(int i=1; i<=N; i++) scanf("%d", &a[i]);
    for(int i=1, u, v; i<N; i++)
    {
        scanf("%d%d", &u, &v);
        addEddge(u, v); addEddge(v, u);
    }
    dfs(1, 0);
    int ans = 0;
    for(int i=0; i<=K; i++) ans = max(ans, dp[1][i]);
    printf("%d\n", ans);
    return 0;
}

 


  The End

Rating changes for the last round are temporarily rolled back. They will be returned soon.

难得要涨超级多的分的说,什么时候才给我还回来啊!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值