此题暴力可解。
时间复杂度证明:
对于每一个t来说,我们最多可以将其分为Σai/t
段区间,之后我们在每一段区间上进行二分,每次找到下一个区间的起点,复杂度是log(Σai)
,因为有 Σai/t
段, 所以总时间复杂度就是 O(Σai/t * log(Σai))
。
这里我们把n定义为Σai。题目中已经给出 Σai≤1e6,所以n≤1e6。
如果我们考虑最坏的情况,m次询问中,t是从1递增到n的,那么总的时间复杂度就应该是(n + n/2 + n/3 + n/4 + ……+n/n) * logn(n)
,也就是n(1 + 1/2 + 1/3 + 1/4 + …… + 1/n) * log(n)
。
那么问题就在于(1 + 1/2 + 1/3 + 1/4 + …… + 1/n)
这个式子有多大呢?
这个东西叫做调和数列求和
,是一个历史性难题,没有公式可以求出准确的值,只有大概的值。
当n有限时候:1+1/2+1/3+……+1/n=lnn,ln是自然对数。
当n趋于无穷时:1+1/2+1/3+……+1/n=lnn+R。 R为欧拉常数,约为0.5772。
在本题中,n显然是有限的,所以总复杂度也就是ln(1e6) * 1e6 * log(1e6) ≈ 14 * 1e6 * 20
,差不多2e8,时间是2s,所以可过。
二分下一个区间的端点的时候,需要判断一下这个端点是不是上一次已经到过的端点,如果是的话,说明一直在同一个区间内跳跃,跳不出去了,这说明ai当中有数比x要大,就是不可能分成小于等于x的段的情况
还有一个需要注意的点是记忆化答案,防止多次询问同种x的时候进行之前已经算过的运算。
#include
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define PII pair
#define PLL pair
#define PIL pair
#define endl '\n'
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1|1
#define IOS std::ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
#define debug(x) cerr << #x <<" = "<< x << endl
#define debug2(x, y) cerr << #x << " = " << x << " and " << #y << " = " << y << endl
#define Pi acos(-1)
using namespace std;
const int mod = 1e9 + 7;
const int N = 2e6 + 10, M = 1e6;
int a[N], sum[N], ans[N];
int main()
{
int n, m;
scanf("%d", &n);
for(int i = 1; i <= n; ++ i) scanf("%d", &a[i]), sum[i] = sum[i - 1] + a[i];
scanf("%d", &m);
while(m --)
{
int x;
scanf("%d", &x);
if(ans[x] != 0)
{
if(ans[x] == -1) puts("Impossible");
else printf("%d\n", ans[x]);
continue;
}
int d = 0, cnt = 1;
int now = upper_bound(sum + 1, sum + 1 + n, d + x) - sum;
d = sum[now - 1];
while(now <= n) //保证搜索的范围不会超过n
{
int now2 = upper_bound(sum + 1, sum + 1 + n, d + x) - sum;
if(now2 == now) //如果上次二分已经到过这个点的话,说明存在ai > x。
{
ans[x] = -1;
break;
}
cnt ++; //段数+1
d = sum[now2 - 1];
now = now2;
}
if(ans[x] != -1) ans[x] = cnt, printf("%d\n", ans[x]);
else puts("Impossible");
}
return 0;
}
给定n个点n-1条边的树,求到m个点的距离相等的点
把m个点放进队列,每次bfs一层,找bfs相同层数时的有m个点可以走到的点。
#include
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define PII pair
#define PLL pair
#define PIL pair
#define endl '\n'
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1|1
#define IOS std::ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
#define debug(x) cerr << #x <<" = "<< x << endl
#define debug2(x, y) cerr << #x << " = " << x << " and " << #y << " = " << y << endl
#define Pi acos(-1)
using namespace std;
const int mod = 1e9 + 7;
const int N = 5e5 + 10;
int n, m;
int h[N], ne[N], e[N], bj[N], belong[N], deep[N], idx; //deep表示当前层数,belong表示继承的点的个数。
vector<int> s;
int ans;
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
void bfs()
{
queue<int> Q;
for(int i = 0; i < s.size(); i ++)
deep[s[i]] = 1, belong[s[i]] = 1, Q.push(s[i]);
while(!Q.empty())
{
int head = Q.front();
Q.pop();
if(bj[head]) continue;
bj[head] = 1;
for(int j = h[head]; ~j; j = ne[j])
{
int to = e[j];
if(deep[to] == 0 || deep[to] == deep[head] + 1) //如果当前点没有被走到过 或者 这个点是同一层的点走到过的点的话
{
belong[to] += belong[head]; //更新在同一层当中有多少个点能走到当前点
deep[to] = deep[head] + 1; //如果当前点没有走过的话,继承层数
if(belong[to] == m) //如果同一层中有m个点可以走到当前点的话,说明当前点就是中心点
{
ans = to;
return;
}
if(!bj[to])
Q.push(to);
}
}
}
return;
}
int main()
{
IOS;
memset(h, -1, sizeof h);
cin >> n >> m;
for(int i = 0; i < n - 1; i ++)
{
int a, b;
cin >> a >> b;
add(a, b);
add(b, a);
}
for(int i = 1; i <= m; i ++)
{
int p;
cin >> p;
s.push_back(p);
}
bfs();
if(n == 1) cout << "YES" << endl << 1 << endl;
else
{
if(ans)
cout << "YES" << endl << ans << endl;
else
cout << "NO" << endl;
}
return 0;
}