牛客练习赛14 B 倍增 E bitset

B 区间的连续段
链接:https://www.nowcoder.com/acm/contest/82/B
来源:牛客网

题目描述

给你一个长为n的序列a和一个常数k

有m次询问,每次查询一个区间[l,r]内所有数最少分成多少个连续段,使得每段的和都 <= k

如果这一次查询无解,输出"Chtholly"
输入描述:

第一行三个数n,m,k
第二行n个数表示这个序列a
之后m行,每行给出两个数l r表示一次询问

输出描述:

输出m行,每行一个整数,表示答案

示例1
输入

5 5 7
2 3 2 3 4
3 3
4 4
5 5
1 5
2 4

输出

1
1
1
2
2

备注:

对于100%的数据,1 <= n , m <= 1000000 , 1 <= ai , k <= 1000000000

tags:
dp[i][j] 表示第 i 个数往后走 2^j 段能到达第几个数,所以转移即 dp[i][j] = dp[dp[i][j-1]][j-1] ,查询时只要倍增跳着找即可。

//https://www.nowcoder.com/acm/contest/82/B
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
#define  mid  (l+(r-l)/2)
typedef long long ll;
const int N = 1000005;

int n, m, dp[N][21], k, a[N];
ll  sum[N];
int solve(int l, int r) {
    int ret = 1;
    for(int j=20; dp[l][0]<=r; --j) {
        if(dp[l][j]<=r) {
            l = dp[l][j];
            ret += (1<<j);
        }
    }
    return ret;
}
int vis[N];
int main()
{
    scanf("%d%d%d", &n, &m, &k);
    int ai;
    rep(i,1,n) {
        scanf("%d", &a[i]), sum[i]=sum[i-1]+a[i];
        vis[i] = vis[i-1]+(a[i]>k);
    }
    mes(dp, INF);
    rep(i,1,n)
    {
        int pos = upper_bound(sum+1, sum+1+n, sum[i-1]+k) - sum;
        dp[i][0] = pos;
    }
    for(int j=0; (1<<j)<=n; ++j)
    {
        rep(i,1,n)
        {
            if(dp[i][j-1]<=n)
                dp[i][j] = dp[dp[i][j-1]][j-1];
        }
    }
    int l, r;
    while(m--)
    {
        scanf("%d%d", &l, &r);
        if(vis[r]-vis[l-1]>0) puts("Chtholly");
        else printf("%d\n", solve(l, r));
    }

    return 0;
}

E 无向图中的最短距离
链接:https://www.nowcoder.com/acm/contest/82/E
来源:牛客网

题目描述

有一个n个点的无向图,有m次查询,每次查询给出一些(xi,yi)

令dist(x,y)表示x和y点在图中最短距离,dist(x,x)=0,如果x,y不连通则dist(x,y) = inf

每次查询图中有多少个点v与至少一个这次询问给出的(xi,yi)满足dist(v,xi)<=yi
输入描述:

第一行三个数表示n,m,q

之后m行每行两个数x,y表示有一条x与y之间的边,边权为1

之后q次询问,每个询问先给你一个数a
之后一行2a个数,第2i-1个数xi和第2i个数yi表示一个二元组(xi,yi)

输出描述:

输出q行,每行一个数表示这次询问的答案

示例1
输入

5 6 6
2 3
1 3
2 5
1 3
3 2
2 5
1
3 1
1
1 1
1
1 4
1
5 2
1
1 4
2
1 0 5 1

输出

3
2
4
3
4
3

备注:

对于100%的数据,n <= 1000 , m <= 100000 , q <=
100000
a的和<= 2100000

tags:
先预处理出每个点到其它 n-1个点的最短距离,然后搞个 bitset<1000>bit[1000][1000] ,比如到点 u 的距离 <= x 的点 to 存入,即 bit[u][x].set(to) ,也就是把距离 <= 或者 > 两种可能性存入进去。 bitset 是按位储存的, bitset<1000> 只有1000位(实际应该会按字节扩充一点),所以是可以储存下的。

//https://www.nowcoder.com/acm/contest/82/E
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 1005;

bitset<N> bit[N][N], bit1;
int n, m;
vector< int > G[N];

int dis[N];  bool inq[N];  queue< int > q;
void spfa(int st)
{
    while(!q.empty()) q.pop();
    mes(dis, INF);
    dis[st] = 0;
    q.push(st);
    while(!q.empty())
    {
        int u = q.front();  q.pop();
        inq[u] = false;
        for(int to : G[u])
        {
            if(dis[to] > dis[u]+1)
            {
                dis[to] = dis[u]+1;
                if(inq[to]==false) inq[to]=true, q.push(to);
            }
        }
    }
    rep(i,1,n) if(dis[i]!=INF) bit[st][dis[i]].set(i);
    rep(i,1,n) bit[st][i] |= bit[st][i-1];
}
int main()
{
    int Q;
    scanf("%d%d%d", &n, &m, &Q);
    int u, v;
    rep(i,1,m)
    {
        scanf("%d%d", &u, &v);
        G[u].PB(v), G[v].PB(u);
    }
    rep(i,1,n) spfa(i);
    int k, x, y;
    while(Q--)
    {
        bit1.reset();
        scanf("%d", &k);
        while(k--)
        {
            scanf("%d%d", &x, &y);
            bit1 |= bit[x][y];
        }
        printf("%d\n", bit1.count());
    }

    return 0;
}

posted @ 2018-04-13 15:47  v9fly  阅读(144)  评论(0编辑  收藏  举报