牛客练习赛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;
}