A 水
B 一条直线上的点之间钻来钻去,优先队列BFS,穷人版最短路
C m在10的15次方,涉及到k的3次方,小范围打表找规律,发现结果n约等于6倍m,确定n的范围在8*10的15次方以内,二分n是20的时间复杂度,check函数是2*10的5次方时间复杂度。
#include <iostream>
#include <cstdio>
using namespace std;
typedef unsigned long long LL;
int main()
{
//freopen("data.out", "w", stdout);
LL l = 1, r = 8000000000000000LL, mid, m, ans = 0;
cin>> m;
while(l <= r){
LL sum = 0;
mid = l + (r-l)/2;
for(LL i = 2; i <= 200000; i++){
LL i3 = i*i*i;
if (i3 > mid) break;
sum += mid / i3;
}
if (sum == m){
ans = mid;
r = mid - 1;
}
else if (sum > m){
r = mid - 1;
}
else{
l = mid + 1;
}
}
if (ans > 0)
cout<<ans<<endl;
else
cout<<-1<<endl;
return 0;
}
D 好题,当l固定,r移动,[l r]区间中(maxa - minb)构成递增序列,二分确定(maxa - minb == 0)时r的区间长度,答案累加,nlogn复杂度。关键:发现max 和 min在序列中变化的规律。
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long LL;
const int maxn = 200100;
int a[maxn], b[maxn];
int ma[maxn][20],mb[maxn][20];
int querya(int l, int r){
int v = log2(r - l + 1) + 0.0000000001;
return max(ma[l][v], ma[r - (1<<v) + 1][v]);
}
int queryb(int l, int r){
int v = log2(r - l + 1) + 0.0000000001;
return min(mb[l][v], mb[r - (1<<v) + 1][v]);
}
int main()
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; i++){
scanf("%d", &b[i]);
}
for(int i = 1; i <= n; i++) ma[i][0] = a[i];
for(int j = 1; j <= 18; j++){
for(int i = 1; i <= n; i++){
ma[i][j] = max(ma[i][j-1], ma[min(n, i + (1<<(j-1)))][j-1]);
}
}
for(int i = 1; i <= n; i++) mb[i][0] = b[i];
for(int j = 1; j <= 18; j++){
for(int i = 1; i <= n; i++){
mb[i][j] = min(mb[i][j-1], mb[min(n, i + (1<<(j-1)))][j-1]);
}
}
LL ans = 0;
for(int i = 1; i <= n; i++){
int l = i, r = n, mid, qa, qb, rmin = 1, rmax = 0;
while(l <= r){
mid = (l+r)>>1;
qa = querya(i, mid);
qb = queryb(i, mid);
if (qa - qb == 0){
rmin = mid;
r = mid - 1;
}
else if (qa - qb > 0){
r = mid - 1;
}
else{
l = mid + 1;
}
}
l = i, r = n;
while(l <= r){
mid = (l+r)>>1;
qa = querya(i, mid);
qb = queryb(i, mid);
if (qa - qb == 0){
rmax = mid;
l = mid + 1;
}
else if (qa - qb > 0){
r = mid - 1;
}
else{
l = mid + 1;
}
}
//cout<<rmax - rmin + 1<<endl;
ans += rmax - rmin + 1;
}
cout<<ans<<endl;
return 0;
}
E 好题,将题意提炼成模型,对于一个insection i,若出现在了p个线段中,由于每次最多选k个线段,则i对最终结果贡献了C(k,p)。对于每一个i分别累加即可,外加一点儿逆元的解法inv(n) = n^(mod - 2),mod为素数。
#include <iostream>
#include <map>
#include <cstdio>
using namespace std;
typedef long long LL;
const int mod = 1000000007;
int f[200010], c[200010];
map<int,int>mp;
int C(int n, int k){
int sum = (1ll * f[n] * c[k]) % mod;
return (1ll * sum * c[n-k]) % mod;
}
int Pow(int a, int b){
int re = 1;
while(b){
if (b & 1) re = (1ll * re * a)%mod;
a = (1ll * a * a)%mod;
b >>= 1;
}
return re;
}
int main()
{
int n, k, l, r;
scanf("%d%d", &n, &k);
for(int i = 0; i < n; i++){
scanf("%d%d", &l, &r);
mp[l]++;
mp[r+1]--;
}
f[0] = c[0] = 1;
for(int i = 1; i <= n; i++){
f[i] = (1ll * f[i-1] * i)%mod;
c[i] = Pow(f[i], mod - 2);
}
int cur = mp.begin()->first, sum = 0;
LL ans = 0;
for(map<int,int>::iterator it = mp.begin(); it != mp.end(); it++){
int len = it->first - cur;
if (sum >= k)ans += (1ll * len * C(sum, k))%mod;
ans = (ans >= mod)?ans-mod:ans;
sum += it->second;
cur = it->first;
//cout << ans <<endl;
}
cout << ans <<endl;
return 0;
}