2019ICPC南昌【个人题解CEGKL】

C - And and Pair(二项式定理)

思路

考虑枚举每一个 i i i,求满足条件的 j j j,容易发现,当 i i i确定时,令 i i i的二进制位从高位到低位第一个1往后0的数量为 x x x,则满足条件的 j j j 2 x 2^x 2x
由于和 i i i最高位的1有关,那么考虑枚举最高位为 b b b的所有 i i i
假设原串在 b b b位之后1的数量为 y y y,0的数量为 x x x,那么最高位为 b b b i i i的数量为 2 y 2^y 2y,那么除了最高位之外还有 z z z个1的 i i i的数量为 C y z C_y^z Cyz,这些 i i i对应的 j j j的数量都为 2 x + ( y − z ) 2^{x+(y-z)} 2x+(yz) 0 ≤ z ≤ y 0 \le z \le y 0zy)
于是,以第 b b b位为最高位的 i i i对应的方案总数为
2 x ( C y 0 2 y + C y 1 2 y − 1 + . . . + C y y 2 0 ) = 2 x ( 1 + 2 ) y = 2 x 3 y 2^x(C_y^02^y+C_y^12^{y-1}+...+C_y^y2^0)=2^x(1+2)^y=2^x3^y 2x(Cy02y+Cy12y1+...+Cyy20)=2x(1+2)y=2x3y
所以从后往前枚举一遍就可以啦

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 100005;
const int mod = 1000000007;

int n;
char s[N];

int qpow(int a, int b)
{
    int res = 1;
    while(b)
    {
        if (b & 1) res = 1LL * res * a % mod;
        a = 1LL * a * a % mod;
        b >>= 1;
    }
    return res;
}

int main()
{
    int _;
    scanf("%d", &_);
    while (_ --)
    {
        scanf("%s", s + 1);
        n = strlen(s + 1);
        int c1 = 0, c0 = 0;
        int ans = 0;
        for (int i = n; i >= 1; i -- )
        {
            if (s[i] == '1')
                ans = (ans + 1LL * qpow(2, c0) * qpow(3, c1) % mod) % mod;
            if (s[i] == '1') c1 ++;
            else c0 ++;
        }

        printf("%d\n", (ans + 1) % mod);
    }
    return 0;
}

E - Bob’s Problem(简单图论)

思路

首先把黑边选满
如果此时图已经联通,则选出k条权值最大的白边
否则对白边做最大生成树
如果最终图仍然不连通,则-1
否则如果白边还可以继续选,选出没被选到的权值最大的几条白边即可
用并查集维护即可

代码

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

typedef long long LL;

const int N = 50005, M = 500005;

int n, m, k;
struct edge
{
    int u, v, w;
    bool operator < (const edge& e) const 
    {
        return w > e.w;
    }
}E[M], unwhite[M], white[M];
int cnt, wcnt, uncnt;

int fa[N];
bool vis[N];

int getf(int x)
{
    return x == fa[x] ? x : fa[x] = getf(fa[x]);
}

int main()
{
    int _;
    scanf("%d", &_);
    while (_ --)
    {
        uncnt = wcnt = cnt = 0;
        scanf("%d%d%d", &n, &m, &k);
        for (int i = 1; i <= n; i ++ ) fa[i] = i;

        LL ans = 0;
        for (int i = 1; i <= m; i ++ )
        {
            int u, v, w, c;
            scanf("%d%d%d%d", &u, &v, &w, &c);
            if (c == 1) white[++ wcnt] = {u, v, w};
            else 
            {
                ans += w;
                u = getf(u), v = getf(v);
                if (u != v)
                    fa[u] = v;
            }
        }

        for (int i = 1; i <= wcnt; i ++ )
        {
            int u = getf(white[i].u), v = getf(white[i].v);
            if (u != v)
                E[++ cnt] = white[i];
            else 
                unwhite[++ uncnt] = white[i];
        }

        sort(E + 1, E + 1 + cnt);
        for (int i = 1; i <= cnt; i ++ ) vis[i] = false;
        for (int i = 1; i <= cnt; i ++ )
        {
            int u = getf(E[i].u), v = getf(E[i].v);
            if (u != v && k)
            {
                vis[i] = true;
                ans += E[i].w;
                fa[u] = v;
                k --;
            }
        }

        bool flag = true;
        for (int i = 1; i <= n; i ++ )
            if (getf(i) != getf(1))
            {
                flag = false;
                break;
            }

        if (!flag)
        {
            puts("-1");
            continue;
        }

        if (k)
        {
            
            for (int i = 1; i <= cnt; i ++ )
                if (!vis[i])
                    unwhite[++ uncnt] = E[i];

            sort(unwhite + 1, unwhite + 1 + uncnt);
            for (int i = 1; i <= k; i ++ )
                ans += unwhite[i].w;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

G - Eating Plan(思维、暴力)

思路

首先需要观察到 998857459 998857459 998857459并不是一个质数,质因数分解之后发现它由三个质数组成,最大的指数为 2803 2803 2803,也就是说, a i ≥ 2803 a_i \ge 2803 ai2803时, a i !   m o d   t = 0 a_i!\ mod\ t=0 ai! mod t=0
故给出的序列中最多只有 2802 2802 2802个不为0的项,那么我们枚举任意两项求出每个长度最多能吃多少质量,然后二分一下就可以了

代码

#include <iostream>
#include <cmath>

using namespace std;

const int N = 100005, M = 3000, mod = 998857459;

int n, m;
int jc[N];
struct Node
{
    int p, val;
}a[N];
int cnt;
int mx[N];
int sum[N];
int b[N], c[N];
int b_cnt;

int main()
{
    scanf("%d%d", &n, &m);

    jc[0] = 1;
    for (int i = 1; i <= n; i ++ )
        jc[i] = 1LL * jc[i - 1] * i % mod;
    
    for (int i = 1; i <= n; i ++ )
    {
        int x;
        scanf("%d", &x);
        if (jc[x])
            a[++ cnt] = {i, jc[x]};
    }

    for (int i = 1; i <= cnt; i ++ )
        sum[i] = (sum[i - 1] + a[i].val) % mod; 

    for (int i = 1; i <= cnt; i ++ )
        for (int j = i; j <= cnt; j ++ )
        {
            int l = a[i].p, r = a[j].p;
            int len = r - l + 1;
            mx[len] = max(mx[len], (sum[j] - sum[i - 1] + mod) % mod);
        }

    int res = -1;
    for (int i = 1; i <= n; i ++ )
        if (mx[i] > res)
            res = mx[i], b[++ b_cnt] = mx[i], c[b_cnt] = i;

    for (int i = 1; i <= m; i ++ )
    {
        int k;
        scanf("%d", &k);
        int p = lower_bound(b + 1, b + 1 + b_cnt, k) - b;
        if (p > b_cnt) puts("-1");
        else 
            printf("%d\n", c[p]);
    }


    return 0;
}

K - Tree(dsu on tree)

思路

启发式合并(dsu on tree)裸题
求满足要求的点对有多少,相当于枚举每一个点 u u u作为 l c a lca lca,求它的任意两颗子树中的任意满足条件的点对数量。
条件有两个,对于两颗不同子树中的点 v 1 v1 v1 v 2 v2 v2而言,
首先要满足 v a l [ v 1 ] + v a l [ v 2 ] = 2 ∗ v a l [ u ] val[v1] + val[v2] = 2 * val[u] val[v1]+val[v2]=2val[u]
然后要满足 d e p [ v 1 ] + d e p [ v 2 ] − 2 ∗ d e p [ u ] < = k dep[v1] + dep[v2] - 2 * dep[u] <= k dep[v1]+dep[v2]2dep[u]<=k
按照dsu on tree的思路,在枚举 v 1 v1 v1时, v 2 v2 v2已经记录起来了,也就是说枚举 v 1 v1 v1然后求满足
v a l [ v 2 ] = 2 ∗ v a l [ u ] − v a l [ v 1 ] val[v2] = 2 * val[u] - val[v1] val[v2]=2val[u]val[v1] d e p [ v 2 ] < = 2 ∗ d e p [ u ] + k − d e p [ v 1 ] dep[v2] <= 2 * dep[u] + k - dep[v1] dep[v2]<=2dep[u]+kdep[v1] v 2 v2 v2数量
那么就可以对每一个值开一颗权值线段树(需要动态开点),每一颗权值线段树以深度作为下标,这样维护就可以啦

代码

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

typedef long long LL;

const int N = 100005;

int n, k;
int val[N];
struct edge
{
	int to, next;
}e[N];
int head[N], cnt;
int siz[N], dep[N], son[N];

struct Tree
{
	int l, r;
	int sum;
}t[N * 50];
int t_cnt;
int rt[N];

LL ans;

void add(int u, int v)
{
	e[++ cnt] = {v, head[u]};
	head[u] = cnt;
}

void push_up(int i)
{
	t[i].sum = t[t[i].l].sum + t[t[i].r].sum;
}

void update(int& i, int l, int r, int pos, int k)
{
	if (!i) i = ++ t_cnt;
	if (l == r)
	{
		t[i].sum += k;
		return;
	}
	int mid = (l + r) >> 1;
	if (pos <= mid) update(t[i].l, l, mid, pos, k);
	else update(t[i].r, mid + 1, r, pos, k);
	push_up(i);
}

int query(int i, int l, int r, int ql, int qr)
{
	if (!i) return 0;
	if (ql <= l && r <= qr)
		return t[i].sum;
	int mid = (l + r) >> 1;
	int s = 0;
	if (ql <= mid) s += query(t[i].l, l, mid, ql, qr);
	if (qr > mid) s += query(t[i].r, mid + 1, r, ql, qr);
	return s; 
}

void dfs(int u, int ff)
{
	dep[u] = dep[ff] + 1;
	siz[u] = 1;
	for (int i = head[u]; i; i = e[i].next)
	{
		int v = e[i].to;
		dfs(v, u);
		siz[u] += siz[v];
		if (!son[u] || siz[v] > siz[son[u]]) son[u] = v;
	}
}

void updateAns(int u, int lca)
{
	int Dep = 2 * dep[lca] + k - dep[u];
	int Val = 2 * val[lca] - val[u];
	if (Val >= 0 && Val <= n && Dep >= dep[lca] + 1)
		ans += query(rt[Val], 1, n, dep[lca] + 1, min(Dep, n));
	for (int i = head[u]; i; i = e[i].next)
	{
		int v = e[i].to;
		updateAns(v, lca);
	}
}

void modify(int u, int k)
{
	update(rt[val[u]], 1, n, dep[u], k);
	for (int i = head[u]; i; i = e[i].next)
		modify(e[i].to, k);
}

void dsu(int u, int k)
{
	for (int i = head[u]; i; i = e[i].next)
	{
		int v = e[i].to;
		if (v == son[u]) continue;
		dsu(v, 0);
	}
	if (son[u]) dsu(son[u], 1);

	for (int i = head[u]; i; i = e[i].next)
	{
		int v = e[i].to;
		if (v == son[u]) continue;
		updateAns(v, u);
		modify(v, 1);
	}

	update(rt[val[u]], 1, n, dep[u], 1);

	if (!k) modify(u, -1);
}

int main()
{
	scanf("%d%d", &n, &k);
	for (int i = 1; i <= n; i ++ ) scanf("%d", &val[i]);
	for (int i = 2; i <= n; i ++ ) 
	{
		int p;scanf("%d", &p);
		add(p, i);
	}

	dfs(1, 0);
	dsu(1, 0);

	printf("%lld\n", ans * 2);

	return 0;
}

L - Who is the Champion(签到)

思路

按照题意计算每只队伍的得分还有得球数与失球数的差之后排序即可。
注意特判 n = = 1 n==1 n==1的情况!!!!(wa了两发)

代码

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

typedef long long LL;

const int N = 105;

int n;
LL b[N][N];
struct Team
{
    LL score, goal, conceded;
    LL diff;
    int id;
    bool operator < (const Team& b) const
    {
        if (score == b.score)
            return diff > b.diff;
        return score > b.score;
    }
}a[N];

int getscore(LL sa, LL sb)
{
    if (sa > sb) return 3;
    else if (sa == sb) return 1;
    else return 0;
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            scanf("%lld", &b[i][j]);
    
    if (n == 1)
    {
        puts("1");
        return 0;
    }

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
        {
            if (i != j)
            {
                a[i].goal += b[i][j];
                a[i].conceded += b[j][i];
                a[i].score += getscore(b[i][j], b[j][i]);
            }
        }

    for (int i = 1; i <= n; i ++ )
        a[i].id = i, a[i].diff = a[i].goal - a[i].conceded;

    sort(a + 1, a + 1 + n);

    if (a[1].score == a[2].score && a[1].diff == a[2].diff)
        puts("play-offs");
    else 
        printf("%d\n", a[1].id);


    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值