T1 能量采集
显然对于每个点采集时(k = gcd(i, j) - 1), 则
[ans = sum_{i = 1}^{n}sum_{j = 1}^{m}(2 * gcd(i, j) - 1) \
ans = 2 * (sum_{i = 1}^{n}sum_{j = 1}^{m}gcd(i, j)) - n * m \
欧拉反演得 \
ans = 2 * (sum_{d = 1}^{n}phi(d)lfloorfrac{n}{d}
floorlfloorfrac{m}{d}
floor) - n * m
]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 100005
int n, m, prime[N], cnt = 0;
ll phi[N];
bool isprime[N];
void Init(int n)
{
isprime[1] = 1;
phi[1] = 1;
for(int i = 2; i <= n; i++)
{
if(!isprime[i]) prime[++cnt] = i, phi[i] = i - 1;
for(int j = 1; j <= cnt && i * prime[j] <= n; j++)
{
isprime[i * prime[j]] = 1;
if(i % prime[j] == 0)
{
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
phi[i * prime[j]] = phi[i] * phi[prime[j]];
}
}
for(int i = 2; i <= n; i++) phi[i] += phi[i - 1];
}
int main()
{
cin >> n >> m;
if(n > m) swap(n, m);
Init(n);
ll ans = 0;
for(int l = 1, r; l <= n; l = r + 1)
{
r = min(n / (n / l), m / (m / l));
ans += (phi[r] - phi[l - 1]) * (n / l) * (m / l);
}
printf("%lld
", ans * 2 - 1ll * n * m);
return 0;
}
T2 超级钢琴
对每个点找出范围里的最大值,然后每次取完之后把两边两段加入,在堆中按区间最大值排序取k个即可。
#include<bits/stdc++.h>
using namespace std;
#define N 500005
typedef long long ll;
int n, k, L, R;
ll a[N];
int st[20][N];
int Log2(int x) {return log(x) / log(2);}
int query(int l, int r)
{
int len = log2(r - l + 1);
return (a[st[len][l]] >= a[st[len][r - (1 << len) + 1]]) ? st[len][l] : st[len][r - (1 << len) + 1];
}
struct point
{
int o, l, r, val;
bool operator < (const point &s) const
{
return a[val] - a[o - 1] < a[s.val] - a[s.o - 1];
}
};
priority_queue <point> pq;
int main()
{
scanf("%d%d%d%d", &n, &k, &L, &R);
for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
for(int i = 2; i <= n; i++) a[i] += a[i - 1];
for(int i = 1; i <= n; i++) st[0][i] = i;
for(int i = 1; i <= 19; i++)
for(int j = 1; j <= n - (i << 1) + 1; j++)
{
if(a[st[i - 1][j]] >= a[st[i - 1][j + (1 << i - 1)]]) st[i][j] = st[i - 1][j];
else st[i][j] = st[i - 1][j + (1 << i - 1)];
// cout << i << ' ' << j << ' ' << a[st[i][j]] << endl;
}
for(int i = 1; i <= n; i++)
{
int l = i + L - 1, r = min(i + R - 1, n);
if(l > n) break;
pq.push((point){i, l, r, query(l, r)});
}
ll ans = 0;
while(k--)
{
point u = pq.top();
pq.pop();
// cout << u.o << ' ' << u.val << endl;
ans += a[u.val] - a[u.o - 1];
int x = u.val;
if(u.l <= x - 1) pq.push((point){u.o, u.l, x - 1, query(u.l, x - 1)});
if(x + 1 <= u.r) pq.push((point){u.o, x + 1, u.r, query(x + 1, u.r)});
}
printf("%lld
", ans);
return 0;
}
T3 海拔
首先发现取值只会是0或1, 然后可知两者都会只有一个联通块,体力消耗就是两个联通块中间连着的那些边。所以起点和终点的最小割就是答案。平面图转对偶图求一求就好了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 350005
int n, head[N], nxt[N << 2], to[N << 2], cnt = 0;
ll val[N << 2];
void add(int x, int y, ll z)
{
cnt++;
to[cnt] = y, val[cnt] = z, nxt[cnt] = head[x], head[x] = cnt;
}
ll dis[N];
bool vis[N];
struct point
{
int x; ll dis;
bool operator < (const point &o) const
{
return dis > o.dis;
}
};
priority_queue <point> q;
int s = 0, t;
ll ans = 0;
void dij()
{
memset(dis, 0x3f, sizeof(dis));
dis[s] = 0;
q.push((point){s, 0});
while(!q.empty())
{
point u = q.top(); q.pop();
int x = u.x;
if(vis[x]) continue;
vis[x] = 1;
for(int p = head[x]; p; p = nxt[p])
{
int v = to[p];
// cout << ' ' << x << ' ' << v << endl;
if(dis[v] > dis[x] + val[p])
{
dis[v] = dis[x] + val[p];
q.push((point){v, dis[v]});
}
}
}
printf("%lld
", dis[t]);
}
int main()
{
scanf("%d", &n);
t = n * n + 1;
for(int i = 1; i <= n + 1; i++)
for(int j = 1; j <= n; j++)
{
int x;
scanf("%d", &x);
int k = (i - 1) * n + j;
// cout << s << ' ' << k << ' ' << t << endl;
if(i == 1) add(k, t, x);
else if(i == n + 1) k -= n, add(s, k, x);
else add(k, k - n, x);
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n + 1; j++)
{
int x;
scanf("%d", &x);
int k = (i - 1) * n + j;
if(j == 1) add(s, k, x);
else if(j == n + 1) k--, add(k, t, x);
else add(k - 1, k, x);
}
for(int i = 1; i <= n + 1; i++)
for(int j = 1; j <= n; j++)
{
int x;
scanf("%d", &x);
int k = (i - 1) * n + j;
if(i == 1) add(t, k, x);
else if(i == n + 1) k -= n, add(k, s, x);
else add(k - n, k, x);
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n + 1; j++)
{
int x;
scanf("%d", &x);
int k = (i - 1) * n + j;
if(j == 1) add(k, s, x);
else if(j == n + 1) k--, add(t, k, x);
else add(k, k - 1, x);
}
// for(int i = s; i <= t; i++)
// {
// cout << i << ' ';
// for(int p = head[i]; p; p = nxt[p])
// cout << to[p] << ' ' << val[p] << ' ';
// cout << endl;
// }
dij();
return 0;
}