BestCoder Round #85 (hdu5804,hdu5805,hdu5806,hdu5807)

Price List

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5804

解题思路:

中文题目:

问题描述
在Byteland一共有nn家商店,编号依次为11nn。每家商店只会卖一种物品,其中第ii家商店的物品单价为v_ivi。

Byteasar每天都会进行一次购物,他会在每家商店购买最多一件物品,当然他也可以选择什么都不买。回家之后,Byteasar会把这一天购物所花的钱的总数记录在账本上。

Byteasar的数学不好,他可能会把花的钱记少,也可能记多。Byteasar并不介意记少,因为这样看上去显得自己没花很多钱。

请写一个程序,帮助Byteasar判断每条记录是否一定记多了。
输入描述
输入的第一行包含一个正整数T(1\leq T\leq 10)T(1T10),表示测试数据的组数。

对于每组数据,第一行包含两个正整数n,m(1\leq n,m\leq 100000)n,m(1n,m100000),表示商店的个数和记录的个数。

第二行包含nn个正整数v_i(1\leq v_i\leq 100000)vi(1vi100000),依次表示每家商店的物品的单价。

接下来mm行,每行包含一个整数q(0\leq q\leq 10^{18})q(0q1018),表示一条记录。
输出描述
对于每组数据,输出一行mm个字符,依次回答每个询问。如果一定记多了,请输出'1',否则输出'0'。
输入样例
1
3 3
2 5 4
1
7
10000
输出样例
001
算法思想:

求出所有数的和sumsum,如果q > sumq>sum那么肯定记多了。

时间复杂度O(n)O(n)

AC代码:

#include <iostream>
#include <cstdio>
using namespace std;

typedef long long ll;

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int x,n,m;
        scanf("%d%d",&n,&m);
        ll sum = 0,tmp;
        for(int i = 0; i < n; ++i){
            scanf("%d",&x);
            sum += x;
        }
        for(int i = 0; i < m;++i){
            scanf("%lld",&tmp);
            if(tmp <= sum)
                putchar('0');
            else
                putchar('1');
        }
        puts("");
    }
    return 0;
}

NanoApe Loves Sequence

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5805

解题思路:

中文题目:

问题描述
退役狗 NanoApe 滚回去学文化课啦!

在数学课上,NanoApe 心痒痒又玩起了数列。他在纸上随便写了一个长度为 nn 的数列,他又根据心情随便删了一个数,这样他得到了一个新的数列,然后他计算出了所有相邻两数的差的绝对值的最大值。

他当然知道这个最大值会随着他删了的数改变而改变,所以他想知道假如全部数被删除的概率是相等的话,差的绝对值的最大值的期望是多少。
输入描述
第一行为一个正整数 TT,表示数据组数。

每组数据的第一行为一个整数 nn。

第二行为 nn 个整数 A_iAi,表示这个数列。

1 \le T \le 10,~3 \le n \le 100000,~1 \le A_i \le 10^91T10, 3n100000, 1Ai109
输出描述
对于每组数据输出一行一个数表示答案。

为防止精度误差,你需要输出答案乘上 nn 后的值。
输入样例
1
4
1 2 3 4
输出样例
6

算法思想:

求出前ii个数里相邻差值的最大值f_ifiiinn里相邻差值的最大值g_igi,那么ans=\sum_{i=1}^n \max(|A_{i-1}-A_{i+1}|,f_{i-1},g_{i+1})ans=i=1nmax(Ai1Ai+1,fi1,gi+1)

时间复杂度O(n)O(n)

AC代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long ll;
const int N = 100005;
int a[N],l[N],r[N];

int main () {
    int T,n;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i = 1; i <= n; ++i){
            scanf ("%d",&a[i]);
        }
        l[1] = r[n] = 0;
        for(int i = 2; i <= n; ++i){
            l[i] = max(l[i-1],abs(a[i]-a[i - 1]));
        }
        for(int i = n - 1; i >= 1; --i){
            r[i] = max(r[i + 1],abs(a[i]-a[i + 1]));
        }
        ll ans = r[2]+l[n - 1];
        for(int i = 2; i < n; ++i){
            ans += max(abs(a[i-1]-a[i+1]),max(l[i-1],r[i+1]));
        }
        printf("%lld\n",ans);
    }
}

NanoApe Loves Sequence Ⅱ

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5806

解题思路:

中文题目:

问题描述
退役狗 NanoApe 滚回去学文化课啦!

在数学课上,NanoApe 心痒痒又玩起了数列。他在纸上随便写了一个长度为 nn 的数列,他又根据心情写下了一个数 mm。

他想知道这个数列中有多少个区间里的第 kk 大的数不小于 mm,当然首先这个区间必须至少要有 kk 个数啦。
输入描述
第一行为一个正整数 TT,表示数据组数。

每组数据的第一行为三个整数 n,m,kn,m,k。

第二行为 nn 个整数 A_iAi,表示这个数列。

1 \le T \le 10,~2 \le n \le 200000,~1 \le k \le n/2,~1 \le m,A_i \le 10^91T10, 2n200000, 1kn/2, 1m,Ai109
输出描述
对于每组数据输出一行一个数表示答案。
输入样例
1
7 4 2
4 2 7 7 6 5 1
输出样例
18

算法思想:

将不小于mm的数看作11,剩下的数看作00,那么只要区间内11的个数不小于kk则可行,枚举左端点,右端点可以通过two-pointer求出。

时间复杂度O(n)O(n)

AC代码:

#include <iostream>
#include <cstdio>
using namespace std;

typedef long long ll;
const int N = 200005;
int a[N];

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n,m,k;
        scanf("%d%d%d",&n,&m,&k);
        for(int i = 1; i <= n; ++i)
            scanf("%d",&a[i]);
        int num = 0,r = 0;
        ll ans = 0;
        for(int i = 1; i <= n; ++i){
            while(num < k && r < n){
                r++;
                num += (a[r]>=m);
            }
            if(num < k)
                break;
            ans += n-r+1;
            num -= (a[i]>=m);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

Keep In Touch

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5807

解题思路:

中文题目:

问题描述
在Byteland一共有nn个城市,编号依次为11nn,同时有mm条单向道路连接着这些城市,其中第ii条道路的起点为u_iui,终点为v_i(1\leq u_i < v_i\leq n)vi(1ui<vin)。

特工团队一共有33名成员:007,008,以及009,他们将要执行qq次秘密任务。

在每次任务中,三人可能会处于三个不同的城市,他们互相之间通过对讲机保持联络。编号为ii的城市的无线电频为w_iwi,如果两个城市的无线电频差值的绝对值不超过KK,那么无线电就可以接通。三个特工每个时刻必须要选择一条道路,走到下一个城市,每条道路都只需要花费11单位时间。

他们可以选择在任意城市终止任务,甚至可以在起点就终止任务,但不允许在道路上终止任务。现在他们想知道,对于每次任务,给定三个人的起始位置,有多少种可能的合法行动方案,使得行动过程中任意在城市的时刻,他们都可以两两联络?

两个方案被视作不同当且仅当至少存在一个人在某一时刻所在的城市不同。

注意:33个特工必须同时结束任务。
输入描述
输入的第一行包含一个正整数T(1\leq T\leq 10)T(1T10),表示测试数据的组数。

对于每组数据,第一行包含四个整数n(1\leq n\leq 50),m(0\leq m\leq \frac{n(n-1)}{2}),K(0\leq K\leq 10^9),q(1\leq q\leq 125000)n(1n50),m(0m2n(n1)),K(0K109),q(1q125000),表示城市数,道路数,允许的差值上限以及任务个数。

第二行包含nn个正整数w_i(1\leq w_i\leq 10^9)wi(1wi109),依次表示每个城市的无线电频。

接下来mm行,每行包含两个正整数u_i,v_i(1\leq u_i < v_i\leq n)ui,vi(1ui<vin),表示一条单向道路,数据保证没有重边。

接下来qq行,每行包含三个正整数x,y,z(1\leq x,y,z\leq n)x,y,z(1x,y,zn),表示一次任务中三个人的起始位置。数据保证在起始位置三个人可以两两联络。
输出描述
对于每组数据,输出qq行,对于每个任务输出方案数。由于答案可能很大,请对998244353998244353取模。
输入样例
1
4 4 2 10
8 8 4 1
1 3
1 4
2 3
2 4
1 1 1
1 1 2
1 2 1
1 2 2
2 1 1
2 1 2
2 2 1
2 2 2
3 3 3
4 4 4
输出样例
3
3
3
3
3
3
3
3
1
1

算法思想:

考虑dp,设f[i][j][k]f[i][j][k]表示三个人分别在i,j,ki,j,k时的方案数,直接转移是O(n^6)O(n6)的。

于是考虑加维,设f[i][j][k][now]f[i][j][k][now]表示三个人分别在i,j,ki,j,k时,目前准备走nownow这个人的方案数,那么转移复杂度就降低到了O(n^4)O(n4)

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 55;
const int MOD = 998244353;
int w[N],a[N][N];
int f[N][N][N],g[N][N][N],h[N][N][N];

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n,m,K,q;
        int x,y,z;
        scanf("%d%d%d%d",&n,&m,&K,&q);
        for(int i = 1; i <= n; ++i)
            scanf("%d",&w[i]);
        memset(a,0,sizeof(a));
        while(m--){
            scanf("%d%d",&x,&y);
            a[y][x] = 1;
        }

        for(int i = n; i >= 1; --i){
            for(int j = n; j >= 1; --j){
                for(int k = n; k >= 1; --k){
                    f[i][j][k] = 1;
                    g[i][j][k] = h[i][j][k] = 0;
                    for(int t = 1; t <= n; ++t){
                        if(a[t][i])
                            f[i][j][k] = (f[i][j][k]+h[t][j][k])%MOD;
                    }
                    for(int t = 1; t <= n; ++t){
                        if(a[t][j])
                            h[i][j][k] = (h[i][j][k]+g[i][t][k])%MOD;
                    }
                    for(int t = 1; t <= n; ++t){
                        if(a[t][k])
                            g[i][j][k] = (g[i][j][k]+f[i][j][t])%MOD;
                    }
                    if(abs(w[i]-w[j]) > K || abs(w[i]-w[k]) > K || abs(w[j]-w[k]) > K)
                        f[i][j][k] = 0;
                }
            }
        }

        while(q--){
            scanf("%d%d%d",&x,&y,&z);
            printf("%d\n",f[x][y][z]);
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值