2017百度之星初赛b

题目链接


01:http://acm.hdu.edu.cn/showproblem.php?pid=6114
02:http://acm.hdu.edu.cn/showproblem.php?pid=6115
03:http://acm.hdu.edu.cn/showproblem.php?pid=6116
04:http://acm.hdu.edu.cn/showproblem.php?pid=6117
05:http://acm.hdu.edu.cn/showproblem.php?pid=6118
06:http://acm.hdu.edu.cn/showproblem.php?pid=6119

一些题解


01 Chess

 题意说是要找在n*m的棋盘上最多的车让他们互相不攻击,并且下一行的车要在上一行的车的右边,问摆放的方案数。
 那么一共就有min(n, m)个车,一共有max(n, m)个位置则答案就是Cmax(n, m)min(n, m)。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL maxn(1000005), mod(1e9 + 7);
LL Jc[maxn];
void calJc()    //求maxn以内的数的阶乘
{
    Jc[0] = Jc[1] = 1;
    for(LL i = 2; i < maxn; i++)
        Jc[i] = Jc[i - 1] * i % mod;
}
/*
//拓展欧几里得算法求逆元
void exgcd(LL a, LL b, LL &x, LL &y)    //拓展欧几里得算法
{
    if(!b) x = 1, y = 0;
    else
    {
        exgcd(b, a % b, y, x);
        y -= x * (a / b);
    }
}
LL niYuan(LL a, LL b)   //求a对b取模的逆元
{
    LL x, y;
    exgcd(a, b, x, y);
    return (x + b) % b;
}
*/
//费马小定理求逆元
LL pow(LL a, LL n, LL p)    //快速幂 a^n % p
{
    LL ans = 1;
    while(n)
    {
        if(n & 1) ans = ans * a % p;
        a = a * a % p;
        n >>= 1;
    }
    return ans;
}
LL niYuan(LL a, LL b)   //费马小定理求逆元
{
    return pow(a, b - 2, b);
}
LL C(LL a, LL b)    //计算C(a, b)
{
    return Jc[a] * niYuan(Jc[b], mod) % mod
        * niYuan(Jc[a - b], mod) % mod;
}
int main()
{
    int t;
    scanf("%d", &t);
    int n, m;
    calJc();
    while(t--)
    {
        scanf("%d%d", &n, &m);
        printf("%d\n", C(max(n, m),min(n, m)));
    }
    return 0;
}

06 小小粉丝度度熊

 题意很简单,给定n个区间,每个区间有L-R已经被覆盖,给定m个填充剂每一个可以覆盖一个数,问最能连续覆盖多少个数。
 直接合并连续或相交的区间再根据区间开始的数由大到小排序。然后跑一遍记录当前消耗和当前连续数,当消耗的填充剂大于m就把当前首部区间去掉。然后每次比较最大值就可以了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define rep(i, s, t) for(int i = s;i <= t;i++)
#define rap(i, s, t) for(int i = s;i >= t;i--)
using namespace std;
struct Node
{
    long long st, en;
}node[100004];
struct Bag
{
    long long st, en;
}beg[100004];
long long n, m;
int cmp(Node a, Node b)
{
        return a.st < b.st;
}
int main()
{
    while(scanf("%I64d%I64d", &n, &m) != EOF)
    {
        rep(i, 1, n)
            scanf("%I64d%I64d", &node[i].st, &node[i].en);
        sort(node + 1, node + 1 + n, cmp);
        int cnt = 0;
        int xen = 0;
        rep(i, 1, n){
            if(node[i].st <= xen&&i != 1){
                if(node[i].en <= xen)
                    continue;
                else
                {
                    beg[cnt].en = node[i].en;
                    xen = node[i].en;
                }
            }
            else
            {
                beg[++cnt].en = node[i].en;
                xen = node[i].en;
                beg[cnt].st = node[i].st;
            }
        }
        long long xh = 0;
        long long ans = 0;
        int beginn = 1;
        for(int i = 1;i <= cnt;i++){
            if(i != 1){
                int lsxh = beg[i].st - beg[i - 1].en - 1;
                xh += lsxh;
            }
            while(xh > m)
            {
                xh -= beg[beginn + 1].st - beg[beginn].en - 1;
                beginn++;
            }
            ans = max(ans, beg[i].en - beg[beginn].st + 1 + m - xh);
        }
        printf("%I64d\n", ans);
    }
    return 0;
}

补题


02 Factory

 暴力加LCA把所有配对全部加入查询,然后标记问题序号,注意这里不能用公共祖先数组作为距离树根变去除,因为有可能这两个点相同且没有后继即ancestor数组不会更新,那么只有在并查集合并的时候把子树的标记用父数代替,不能像以前这样随意了233333.

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#define rep(i, s, t) for(int i = s;i <= t;i++)
#define rap(i, s, t) for(int i = s;i >= t;i--)
#define inf 0x3f3f3f3f
using namespace std;
int n, m;
const int M = 100004;
vector<int>tree[M];
vector<int>query[M];
vector<int>queid[M];
vector<int>worth[M];
vector<int>G[M];
bool vis[M];
int f[M];
//int ancestor[M];
int dis[M];
int ans[M];
int Q;
int findd(int x)
{
    return x == f[x]?f[x]:f[x] = findd(f[x]);
}
void uni(int x, int y)
{
    x = findd(x);
    y = findd(y);
    if(x != y)
        f[y] = x;
}
void init()
{
    rep(i, 0, n){
        G[i].clear();
        vis[i] = false;
        tree[i].clear();
        query[i].clear();
        worth[i].clear();
        queid[i].clear();
        f[i] = i;
        dis[i] = 0;
        ans[i] = inf;
    }
}
void input_tree()
{
    rep(i, 1, n - 1){
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        tree[a].push_back(b);
        worth[a].push_back(c);
        tree[b].push_back(a);
        worth[b].push_back(c);
    }
}
void input_query()
{
    scanf("%d", &Q);
    rep(i, 1, Q)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        rep(j, 0, (int)G[a].size() - 1)
        {
            rep(k, 0, (int)G[b].size() - 1)
            {
                query[G[a][j]].push_back(G[b][k]);
                queid[G[a][j]].push_back(i);
                query[G[b][k]].push_back(G[a][j]);
                queid[G[b][k]].push_back(i);
            }
        }
    }
}
void tarjan(int x, int value)
{
    dis[x] = value;
    vis[x] = true;
    rep(i, 0, (int)tree[x].size() - 1){
        int v = tree[x][i];
        if(vis[v] == true)
            continue;
        tarjan(v, value + worth[x][i]);
        uni(x, v);
        //ancestor[findd(x)] = x;
    }
    rep(i, 0, (int)query[x].size() - 1){
        int v = query[x][i];
        if(vis[v]&&ans[queid[x][i]] > dis[x] + dis[v] - 2 * dis[findd(v)])
            ans[queid[x][i]] = dis[x] + dis[v] - 2 * dis[findd(v)];
        //printf("%d %d %d %d %d %d %d %d\n", x, v, findd(v), dis[x], dis[v], dis[findd(v)], queid[x][i], ans[queid[x][i]]);
    }
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &m);
        init();
        input_tree();
        rep(i, 1, m){
            int a;
            scanf("%d", &a);
            rep(j, 1, a){
                int b;
                scanf("%d", &b);
                G[i].push_back(b);
            }
        }
        input_query();
        tarjan(1, 0);
        rep(i, 1, Q)
            printf("%d\n", ans[i]);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值