目录
问题 A: sciorz画画
问题 B: 奎奎发红包
问题 C: 关于我转生变成史莱姆这档事
问题 D: 大数
问题 E: Ktree
问题 F: 求和
问题 G: 奎奎画画
问题 H: qiqi and sciorz
问题 I: 星区划分
问题 J: 加油2020
问题 K: 数学问题
求凸包最优三角抛分,三角权函数为 w(i, j, k) = a[i] * a[j] * a[k].
裸题直接上
小吐槽:这 100 组数据是假的...
#include
using namespace std;
typedef long long ll;
const int M = (int)1e2;
const ll inf = 0x3f3f3f3f3f3f3f3f;
int a[M + 5];
ll dp[M + 5][M + 5];
int main()
{
int T;
scanf("%d", &T);
for(int ca = 1; ca <= T; ++ca)
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
memset(dp, 0, sizeof(dp));
for(int len = 3; len <= n; ++len)
{
for(int l = 1; l + len - 1 <= n; ++l)
{
int r = l + len - 1;
for(int k = l + 1; k <= r - 1; ++k)
{
dp[l][r] = max(dp[l][r], dp[l][k] + dp[k][r] + 1ll * a[l] * a[k] * a[r]);
}
}
}
printf("Case #%d: %lld\n", ca, dp[1][n]);
}
return 0;
}
有 n 个人,每人有两个属性 v[i], t[i].
n 个人按照一定顺序前来,第 i 个人的花费为 v[s[i]] * (t[s[1]] + t[s[2]] + ... + t[s[i]]).
求 n 个人的最小花费.
简单贪心,临项交换法即可证明正确性.
#include
using namespace std;
typedef long long ll;
const int M = (int)1e5;
const ll inf = 0x3f3f3f3f3f3f3f3f;
struct node
{
int v, t;
}s[M + 5];
bool cmp(node a, node b)
{
return a.t * b.v < a.v * b.t;
}
int main()
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
{
scanf("%d %d", &s[i].v, &s[i].t);
}
sort(s + 1, s + n + 1, cmp);
int t = 0;
ll ans = 0;
for(int i = 1; i <= n; ++i)
{
t += s[i].t;
ans += 1ll * t * s[i].v;
}
printf("%lld\n", ans);
return 0;
}
一个沙雕要吃 s 坨屎,第一天任意吃但不能全吃完,第 i 天的食屎量必须是第 i - 1 天的 2~9 整数倍,最后一天需要恰好吃完.
求最少食屎天数.
设第一天吃 x,第 i 天吃的为第 i - 1 的 a[i] 倍.
暴搜就完事~
#include
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
int dfs(int s)
{
if(s >= 2 && s <= 9) return 1;
int ans = inf;
for(int i = 2; i <= 9; ++i)
{
if(s % i == 0)
{
ans = min(ans, dfs(s / i - 1) + 1);
}
}
return ans;
}
int main()
{
int s, ans = inf;
scanf("%d", &s);
for(int i = 1; 1ll * i * i <= s; ++i)
{
if(s % i == 0)
{
ans = min(ans, dfs(s / i - 1) + 1);
if(i != s / i) ans = min(ans, dfs(i - 1) + 1);
}
}
printf("%d\n", ans == inf ? -1 : ans);
return 0;
}
求字符串的最小循环节,不存在输出 -1.
枚举长度 n 的约数,暴力 check 即可.
#include
using namespace std;
typedef long long ll;
const int M = (int)1e6;
const ll inf = 0x3f3f3f3f3f3f3f3f;
int n;
string s;
vector fac;
bool check(int l)
{
for(int i = 0; i + l - 1 < n; i += l)
{
if(s.substr(0, l) != s.substr(i, l)) return 0;
}
return 1;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> s;
n = s.size();
for(int i = 1; 1ll * i * i <= n; ++i)
{
if(n % i == 0)
{
fac.push_back(i);
if(i != n / i) fac.push_back(n / i);
}
}
sort(fac.begin(), fac.end());
for(auto x: fac)
{
if(x != n && check(x))
{
cout << s.substr(0, x) << endl;
return 0;
}
}
cout << -1 << endl;
return 0;
}
给出一棵树以及所有边权的和,现让你给每条边分配权值,使得树的直径最小.
求树的最小直径.
由于直径一定是连接两个叶子结点的路径,简单贪心可得最优策略为每个叶子结点平均分摊权值和.
#include
using namespace std;
const int M = (int)1e5;
int de[M + 5];
int main()
{
int n, s;
scanf("%d %d", &n, &s);
for(int i = 0, u, v; i < n - 1; ++i)
{
scanf("%d %d", &u, &v);
de[u]++, de[v]++;
}
int cnt = 0;
for(int i = 1; i <= n; ++i) cnt += (de[i] == 1);
printf("%.2f\n", 2.0 * s / cnt);
return 0;
}
给出 n 阶矩阵 A,求
构造如下矩阵
#include
using namespace std;
typedef long long ll;
const int mod = (int)1e9 + 7;
int n, m, t;
struct Matrix
{
int mat[60][60];
}B;
Matrix mul(Matrix A, Matrix B)
{
Matrix C;
memset(C.mat, 0, sizeof(C.mat));
for(int i = 0; i < t; ++i)
{
for(int j = 0; j < t; ++j)
{
for(int k = 0; k < t; ++k)
{
C.mat[i][j] = (C.mat[i][j] + 1ll * A.mat[i][k] * B.mat[k][j]) % mod;
}
}
}
return C;
}
Matrix quick(Matrix A, int b)
{
Matrix Sum = A;
--b;
while(b)
{
if(b & 1) Sum = mul(Sum, A);
A = mul(A, A);
b >>= 1;
}
return Sum;
}
int main()
{
scanf("%d %d", &n, &m);
t = n * 2;
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < n; ++j)
{
scanf("%d", &B.mat[i][j]);
B.mat[i][j + n] = B.mat[i][j] %= mod;
B.mat[i + n][j + n] = (i == j);
}
}
B = quick(B, m);
for(int i = 0; i < n; ++i)
{
for(int j = n; j < t; ++j)
printf("%d%c", B.mat[i][j], j == t - 1 ? '\n' : ' ');
}
return 0;
}
n * m 的矩阵,初始格均为白色,q 次操作,每次操作选择平行于 x 轴或 y 轴的线段并染为黑色,求每次操作后的白色格连通块数.
先进行 q 次操作,同时记录 (i, j) 点被覆盖的次数.
之后遍历矩阵,找到一个白色格便搜索其四周是否有白色格,并查集标记连通块.
之后依次撤销 q 次操作,操作与上面相似.
详见代码.
#include
using namespace std;
const int M = (int)1e3;
const int N = (int)1e4;
int n, m, q, cnt;
int fa[M * M + 5];
int st[N + 5], top;
int c[M + 5][M + 5];
int x1[N + 5], y1[N + 5];
int x2[N + 5], y2[N + 5];
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
int tofind(int x)
{
if(x == fa[x]) return x;
return fa[x] = tofind(fa[x]);
}
void work(int x, int y)
{
int xx, yy, u, v;
for(int i = 0; i < 4; ++i)
{
xx = x + dx[i];
yy = y + dy[i];
if(xx >= 1 && xx <= n && yy >= 1 && yy <= m && !c[xx][yy])
{
u = (x - 1) * m + y;
v = (xx - 1) * m + yy;
u = tofind(u), v = tofind(v);
if(u != v)
{
--cnt;
fa[u] = v;
}
}
}
}
int main()
{
scanf("%d %d %d", &n, &m, &q);
for(int i = 1; i <= q; ++i)
{
scanf("%d %d %d %d", &x1[i], &y1[i], &x2[i], &y2[i]);
for(int x = x1[i]; x <= x2[i]; ++x)
{
for(int y = y1[i]; y <= y2[i]; ++y)
{
c[x][y]++;
}
}
}
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
fa[(i - 1) * m + j] = (i - 1) * m + j;
}
}
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
if(!c[i][j])
{
++cnt;
work(i, j);
}
}
}
for(int i = q; i >= 1; --i)
{
st[++top] = cnt;
for(int x = x1[i]; x <= x2[i]; ++x)
{
for(int y = y1[i]; y <= y2[i]; ++y)
{
--c[x][y];
}
}
for(int x = x1[i]; x <= x2[i]; ++x)
{
for(int y = y1[i]; y <= y2[i]; ++y)
{
if(!c[x][y])
{
++cnt;
work(x, y);
}
}
}
}
while(top) printf("%d\n", st[top--]);
return 0;
}
K 倍博弈.
#include
using namespace std;
const int M = (int)1e5;
int a[M + 5];
int b[M + 5];
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
int n, k;
scanf("%d %d", &n, &k);
int i = 0, j = 0;
a[i] = b[j] = 1;
while(a[i] < n)
{
++i;
a[i] = b[i - 1] + 1;
while(k * a[j + 1] < a[i]) ++j;
if(k * a[j] < a[i]) b[i] = a[i] + b[j];
else b[i] = a[i];
}
if(a[i] == n)
{
printf("qiqi lose\n");
continue;
}
int ans;
while(n)
{
if(n >= a[i])
{
n -= a[i];
ans = a[i];
}
--i;
}
printf("%d\n", ans);
}
return 0;
}
三维坐标中给出 n 个点,每个点有权值 v.
定义两个点为同一系当且仅当两个点满足上下左右前后之一相邻 且 权值差的绝对值不超过 m.
关系具有传递性,求共有多少个系.
N^2 枚举 + 并查集
#include
using namespace std;
typedef long long ll;
const int M = (int)1e3;
const ll inf = 0x3f3f3f3f3f3f3f3f;
int x[M + 5];
int y[M + 5];
int z[M + 5];
int v[M + 5];
int fa[M + 5];
ll dist(int i, int j)
{
ll dis = 0;
dis += (ll)abs(x[i] - x[j]);
dis += (ll)abs(y[i] - y[j]);
dis += (ll)abs(z[i] - z[j]);
return dis;
}
int cal(int i, int j)
{
return (int)abs(v[i] - v[j]);
}
int tofind(int x)
{
if(x == fa[x]) return x;
return fa[x] = tofind(fa[x]);
}
int main()
{
int n, m;
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; ++i)
{
fa[i] = i;
scanf("%d %d %d %d", &x[i], &y[i], &z[i], &v[i]);
}
for(int i = 1; i <= n; ++i)
{
for(int j = i + 1; j <= n; ++j)
{
if(dist(i, j) <= 1 && cal(i, j) <= m)
{
int u = tofind(i);
int v = tofind(j);
if(u != v)
{
fa[u] = v;
}
}
}
}
int cnt = 0;
for(int i = 1; i <= n; ++i) cnt += (fa[i] == i);
printf("%d\n", cnt);
return 0;
}
#include
using namespace std;
typedef long long ll;
const int M = (int)1e3;
const int N = (int)2019;
int n; ll p;
bool is_prime[N + 5];
int cnt, prime[N + 5];
int tot;
struct node
{
int p, c;
}fac[M + 5];
ll f[M + 5];
ll g[M + 5];
void get_prime()
{
memset(is_prime, 1, sizeof(is_prime));
is_prime[0] = is_prime[1] = 0;
for(int i = 2; i <= N; ++i)
{
if(is_prime[i]) prime[++cnt] = i;
for(int j = 1; j <= cnt && i * prime[j] <= N; ++j)
{
is_prime[i * prime[j]] = 0;
if(i % prime[j] == 0) break;
}
}
}
ll mul(ll a, ll b)
{
ll sum = 0;
while(b)
{
if(b & 1) sum = (sum + a) % p;
a = (a + a) % p;
b >>= 1;
}
return sum;
}
void get_fac()
{
for(int i = 1; i <= cnt && prime[i] <= N; ++i)
{
int cnt = 0;
int p = prime[i];
while(p <= N)
{
cnt += N / p;
if(1ll * p * prime[i] > N) break;
p *= prime[i];
}
if(cnt) fac[++tot].p = prime[i], fac[tot].c = cnt;
}
}
void get_f()
{
get_prime();
get_fac();
for(int i = 1; i <= n; ++i)
{
f[i] = 1;
for(int j = 1; j <= tot; ++j)
{
f[i] = mul(f[i], i * fac[j].c + 1);
}
}
}
ll quick(ll a, ll b)
{
ll sum = 1;
while(b)
{
if(b & 1) sum = mul(sum, a);
a = mul(a, a);
b >>= 1;
}
return sum;
}
ll inv(int a)
{
return quick(a, p - 2);
}
ll get_sum(ll a, int b)
{
return mul(a, inv(b));
}
void get_g()
{
ll a = 673, b = 3;
for(int i = 1; i <= n; ++i)
{
a = mul(a, 673), b = mul(b, 3);
g[i] = mul(get_sum(a - 1, 673 - 1), get_sum(b - 1, 3 - 1));
}
}
int main()
{
scanf("%d %lld", &n, &p);
get_f();
get_g();
ll ans = 0;
for(int i = 1; i <= n; ++i) ans = (ans + mul(f[i], g[i])) % p;
printf("%lld\n", ans);
return 0;
}
给一个整数 g,接下来 T 组询问,每组给出 n 和 m.
求有多少个 (i, j) 对,使得 g 能整除 ,其中0 ≤ i ≤ n,0 ≤ j ≤ min(i,m).
O(2000 * 2000)预处理出所有组合数及其前缀对和,O(1)查询.
#include
using namespace std;
typedef long long ll;
const int M = (int)2e3;
const int mod = (int)1e9 + 7;
int T, g;
int c[M + 5][M + 5];
int s[M + 5][M + 5];
void init()
{
for(int i = 1; i <= M + 1; ++i)
{
c[i][1] = 1 % g;
for(int j = 2; j <= i; ++j)
{
c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % g;
}
}
for(int i = 1; i <= M + 1; ++i)
{
for(int j = 1; j <= i; ++j)
{
c[i][j] = !c[i][j];
}
}
for(int i = 1; i <= M + 1; ++i)
{
for(int j = 1; j <= M + 1; ++j)
{
s[i][j] = s[i][j - 1] + s[i - 1][j] - s[i - 1][j - 1] + c[i][j];
}
}
}
int main()
{
scanf("%d %d", &T, &g);
init();
while(T--)
{
int n, m;
scanf("%d %d", &n, &m);
printf("%d\n", s[n + 1][m + 1]);
}
return 0;
}