K
发现即是求 k ∗ ( k + 1 ) / 2 > = m a x ( 0 , b 1 − k ∗ a 1 ) + m a x ( 0 , b 2 − k ∗ a 2 ) k*(k+1)/2>=max(0, b1-k*a1)+max(0,b2-k*a2) k∗(k+1)/2>=max(0,b1−k∗a1)+max(0,b2−k∗a2)
单调函数,求出最小满足的k二分即可
#include
using namespace std;
const int maxn = 1e3 + 5;
typedef long long ll;
ll a1, a2, b1, b2;
int main()
{
scanf("%lld%lld", &a1, &a2);
int n; scanf("%d", &n);
ll ans = 1e8;
for (int i = 0; i < n; ++i) {
scanf("%lld%lld", &b1, &b2);
ll l = 1, r = 1e8;
while (l < r) {
ll mid = (l + r) / 2;
if (mid * (mid + 1) >= 2 * (max(b1 - mid * a1, 0ll) + max(b2 - mid * a2, 0ll)))
r = mid;
else
l = mid + 1;
}
ans = min(ans, l);
}
printf("%lld\n", ans);
}
H
每轮选择两个数字的概率为 1 n ∗ ( n − 1 ) 1\over n*(n-1) n∗(n−1)1,考虑顺序还需要乘2,则我们处理出每个数字互质的欧拉函数,求出欧拉函数和sum
则答案即为 1 n ∗ ( n − 1 ) ∗ 2 ∗ ( n / 2 ) ∗ s u m {1\over n*(n-1)}*2*(n/2)*sum n∗(n−1)1∗2∗(n/2)∗sum
#include
using namespace std;
typedef long long ll;
const int maxn = 5000 + 5;
vector prime;
int vis[maxn], phi[maxn];
typedef long long ll;
void get() {
phi[1] = 1;
for (int i = 2; i < maxn; ++i) {
if (!vis[i]) {
prime.push_back(i);
phi[i] = i - 1;
}
for (int j = 0; j < prime.size() && i * prime[j] < maxn; ++j) {
if (i % prime[j] == 0) {
vis[i * prime[j]] = 1;
phi[i * prime[j]] = phi[i] * prime[j];
}
else {
vis[i * prime[j]] = 1;
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
get();
int n; cin >> n;
if (n == 1) {
cout << "0/1" << endl;
return 0;
}
ll ans = 0, rot = 0;
for (int i = 2; i <= n; ++i)
ans += phi[i];
rot = 1ll * n * (n - 1);
ans *= 2ll * (n / 2);
ll gcd = __gcd(ans, rot);
cout << ans / gcd << '/' << rot / gcd << endl;
}
G
注意到n和m很小,且发现走完每个格子后的格子都是 n ∗ m n*m n∗m的贡献
所以暴力求出便利所有格子的最大贡献
#include
using namespace std;
const int maxn = 12 + 2;
typedef long long ll;
int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
int a[maxn][maxn];
int n, m, s, t; ll k, K;
ll solve(int x, int y, ll kk) {
if (kk == K)
return a[x][y];
ll rhs = 0; int tmp = a[x][y];
a[x][y] = 0;
for (int i = 0; i < 4; ++i) {
int xx = x + dir[i][0];
int yy = y + dir[i][1];
if (xx <= 0 || xx > n)
continue;
if (yy <= 0 || yy > m)
continue;
rhs = max(rhs, solve(xx, yy, kk + 1));
}
a[x][y] = tmp;
return tmp + rhs;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m >> s >> t >> k;
K = min(k, 1ll * n * m);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j)
cin >> a[i][j];
}
ll ans = solve(s, t, 1) + K * (K - 1) / 2 + n * m * (k - K);
cout << ans << endl;
}
L
只需要考虑每个点的存在入边的点,用二进制保存其他点是否到该点存在入边关系
用二进制保存所有点的黑白关系记为状态,则当前状态与该点的边关系的和(&)后二进制串中的1的数量决定了该点下一个颜色
考虑最多 2 20 2^{20} 220个状态,会出现环;我们处理出环的开始端点和长度;
前缀和处理未到环的路径能形成的黑色点个数;前缀和处理一个环能形成的黑色点个数;二分求出最小满足情况
#include
using namespace std;
const int maxn = 20 + 2;
const int maxm = 1 << maxn;
typedef long long ll;
int g[maxn], vis[maxm], b[maxm], cnt;
int c0[maxm][maxn], c1[maxm][maxn];
int main()
{
ios::sync_with_stdio(false); cin.tie(0);
int n, m, q; cin >> n >> m >> q;
int s = 0;
for (int i = 0; i < n; ++i) {
int x; cin >> x;
s |= x << i;
}
for (int i = 0; i < m; ++i) {
int u, v; cin >> u >> v; u--, v--;
g[v] |= 1 << u;
}
memset(vis, -1, sizeof(vis));
vis[s] = 0; b[0] = s;
int st, loop;
while (true)
{
int ns = 0;
for (int i = 0; i < n; ++i) {
ns |= (__builtin_parity(g[i] & s) << i);
}
b[++cnt] = s = ns;
if (~vis[s]) {
st = vis[s];
loop = cnt - st;
break;
}
vis[s] = cnt;
}
for (int i = 0; i <= st; ++i)
for (int j = 0; j < n; ++j)
c0[i][j] = (b[i] >> j & 1);
for (int i = 1; i <= st; ++i)
for (int j = 0; j < n; ++j)
c0[i][j] += c0[i - 1][j];
for (int i = 1; i <= loop; ++i)
for (int j = 0; j < n; ++j)
c1[i][j] = c1[i - 1][j] + (b[i + st] >> j & 1);
int x; ll l, r, mid, k;
while (q--) {
cin >> x >> k; x--;
if (k <= c0[st][x]) {
//cout << "case 1" << endl;
l = 0, r = st;
while (l != r) {
mid = (l + r) >> 1;
if (c0[mid][x] < k)
l = mid + 1;
else
r = mid;
}
cout << l << endl;
}
else {
//cout << "case 2 " << st << endl;
k -= c0[st][x];
ll ans = st;
if (c1[loop][x] == 0)
cout << -1 << endl;
else {
if (k >= c1[loop][x]) {
if (k % c1[loop][x]) {
ans += k / c1[loop][x] * loop;
k %= c1[loop][x];
}
else {
ans += (k / c1[loop][x] - 1) * loop;
k = c1[loop][x];
}
}
l = 1, r = loop;
while (l != r) {
mid = (l + r) >> 1;
if (c1[mid][x] < k)
l = mid + 1;
else
r = mid;
}
cout << ans + l << endl;
}
}
}
}
A
考虑对答案的影响因素是 k k k的大小,且每对左右端点 ( i , j ) (i,j) (i,j)对在 a [ i ] < k < a [ j ] a[i]a[i]<k<a[j] 贡献为 2 n − 1 − i − j = 2 n − j ∗ 2 i − 1 2^{n-1-i-j}=2^{n-j}*2^{i-1} 2n−1−i−j=2n−j∗2i−1
设 a n s k ans_k ansk为k时的答案,则对 a n s k + 1 ans_{k+1} ansk+1,我们需要减掉只对 k k k产生贡献的答案和加上只对 k + 1 k+1 k+1产生贡献的答案
我们计算 δ k = a n s k − a n s k − 1 \delta k =ans_k-ans_{k-1} δk=ansk−ansk−1
只对 k k k产生贡献的情况为 ( 1 , k + 1 ) , ( 2 , k + 1 ) , . . . , ( k − 1 , k + 1 ) (1,k+1),(2,k+1),...,(k-1,k+1) (1,k+1),(2,k+1),...,(k−1,k+1)
只对 k + 1 k+1 k+1产生贡献的情况为 ( k , k + 2 ) , ( k , k + 3 ) , . . . , ( k , n ) (k,k+2),(k,k+3),...,(k,n) (k,k+2),(k,k+3),...,(k,n)
因此正倒遍历序列,将每个数x作为右端点i,则计算小于x的贡献为sum,则对 δ x \delta x δx产生 − s u m ∗ 2 n − i -sum*2^{n-i} −sum∗2n−i,计算该数作为左端点i,则计算大于x的贡献为sum,则对 δ ( x + 1 ) \delta (x+1) δ(x+1)产生 s u m ∗ 2 n − i sum*2^{n-i} sum∗2n−i
#include
using namespace std;
const int maxn = 1e5 + 5;
typedef long long ll;
const ll mod = 1e9 + 7;
ll sub(ll a, ll b) {
return a - b < 0 ? a - b + mod : a - b;
}
ll add(ll a, ll b) {
return a + b >= mod ? a + b - mod : a + b;
}
ll tr[maxn << 1];
int lowbit(int x) {
return x & -x;
}
void update(int pos, int val, int n) {
for (int i = pos; i <= n; i += lowbit(i)) {
tr[i] = add(tr[i], val);
}
}
ll sum(int pos) {
ll rhs = 0;
for (int i = pos; i; i -= lowbit(i))
rhs = add(rhs, tr[i]);
return rhs;
}
ll mul[maxn], delta[maxn];
int a[maxn];
int main()
{
ios::sync_with_stdio(false); cin.tie(0);
int n; cin >> n;
mul[0] = 1;
for (int i = 1; i <= n; ++i) {
mul[i] = mul[i - 1] * 2 % mod;
cin >> a[i];
}
for (int i = 1; i <= n; ++i) {
ll val = sum(a[i] - 1) * mul[n - i] % mod;
delta[a[i]] = sub(delta[a[i]], val);
val = sub(sum(n), sum(a[i])) * mul[n - i] % mod;
delta[a[i] + 1] = add(delta[a[i] + 1], val);
update(a[i], mul[i - 1], n);
}
for (int i = 1; i <= (n << 1); ++i)
tr[i] = 0;
for (int i = n; i >= 1; --i) {
ll val = sub(sum(n), sum(a[i])) * mul[i - 1] % mod;
delta[a[i] + 1] = add(delta[a[i] + 1], val);
val = sum(a[i] - 1) * mul[i - 1] % mod;
delta[a[i]] = sub(delta[a[i]], val);
update(a[i], mul[n - i], n);
}
for (int i = 1; i <= n; ++i) {
delta[i] = add(delta[i - 1], delta[i]);
cout << delta[i] << '\n';
}
}