【题目链接】
A - LL
简单题。
#include <bits/stdc++.h>
using namespace std;
int T;
char s[2000];
int main() {
while(gets(s)) {
int len = strlen(s);
for(int i = 0; s[i]; i ++) {
if(s[i] >= 'A' && s[i] <= 'Z') {
s[i] = s[i] - 'A' + 'a';
}
}
if(len != 8 || (strcmp("lovelive", s) != 0)) {
printf("no\n");
}else {
printf("yes\n");
}
}
return 0;
}
B - 奇怪的加法
高精度加法一下,不要进位就好了。
#include <bits/stdc++.h>
using namespace std;
char s[20];
char t[20];
char ans[20];
int limit;
int main() {
while(~scanf("%s%s",s,t)) {
limit = 20;
int lens = strlen(s);
int lent = strlen(t);
for(int i = 0; i < lens / 2; i ++) {
swap(s[i], s[lens - i - 1]);
}
for(int i = 0; i < lent / 2; i ++) {
swap(t[i], t[lent - i - 1]);
}
//printf("%s %s\n", s, t);
for(int i = lens; i < limit; i ++) {
s[i] = '0';
}
for(int i = lent; i < limit; i ++) {
t[i] = '0';
}
for(int i = 0; i < limit; i ++) {
int nums = s[i] - '0';
int numt = t[i] - '0';
ans[i] = (char)('0' + ((nums + numt) % 10));
}
int pos = 0;
for(int i = 19; i >= 0; i --) {
if(ans[i] != '0') {
pos = i;
break;
}
}
for(int i = pos; i >= 0; i --) {
printf("%c", ans[i]);
}
printf("\n");
}
return 0;
}
C - 取手机
总共排列方案有 ${ C }_{ a+b }^{ a }$ 种,第 $k$ 位是 b 手机的方案有 ${ C }_{ a+b-1 }^{ a }$ 种,因此概率为 $\frac { { C }_{ a+b-1 }^{ a } }{ { C }_{ a+b }^{ a } } =\frac { b }{ a+b } $。
#include <bits/stdc++.h>
using namespace std;
int T;
int main() {
scanf("%d", &T);
while(T --) {
long long a, b, k;
scanf("%lld%lld%lld", &a,&b,&k);
printf("%.3f\n", 1.0*b/(a+b));
}
return 0;
}
D - zzq的离散数学教室1
$b=a\times prime$,因此枚举 $prime$,看有多少 $(a,b)$ 满足即可,算一下发现方案数是 $\left\lfloor \frac { R }{ prime } \right\rfloor -L+1$。
这种做法的复杂度为 $O(P \times Q)$。其中 $P$ 为素数个数,$Q$ 为询问次数。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e6 + 10;
int p[N], a[N], b[N];
int ans[N],tt=0;
int f[N];
int t, u;
void init(){
t = 0, u = 0;
for (int i = 2;i < N;i++) {
if (a[i] == 0) p[t++] = i, a[i] = i;
for (int j = 0;j < t && i * p[j] < N;j++) {
a[i*p[j]] = p[j];
if (i % p[j] == 0) {
break;
}
}
}
//printf("%d\n", t);
}
int main(){
init();
int L, R;
while(~scanf("%d%d", &L, &R)) {
int ans = 0;
for(int i = 0; i < t; i ++) {
if(L * p[i] > R) break;
ans = ans + R / p[i] - L + 1;
}
printf("%d\n", ans);
}
return 0;
}
这题询问出到 $10^5$ 次也可以搞。
假设有一堆满足条件的二元组 $(a,b)$,对于一次询问 $(L, R)$,我们需要寻找有多少个二元组满足 $a$ 和 $b$ 均在 $[L,R]$ 内,这个就是很经典的问题了,离线很容易操作,要求在线的话就主席树。这里我们可以处理出所有满足条件的二元组,不会很多,因为 $b$ 的素因子个数就不多。
类似的题目推荐:ZOJ 4008
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int p[N], a[N], b[N];
int ans[N],tt=0;
int f[N];
struct point{
int l,r,id;
}x[N];
int low(int x) {
return x&(-x);
}
void add(int x) {
for (int i = x;i<N;i+=low(i)) {
f[i]++;
}
}
int sum(int x) {
int res = 0;
for (int i = x;i > 0;i-=low(i)) {
res+=f[i];
}
return res;
}
void init(){
int t = 0, u = 0;
for (int i = 2;i < N;i++) {
if (a[i] == 0) p[t++] = i, a[i] = i;
for (int j = 0;j < t && i * p[j] < N;j++) {
a[i*p[j]] = p[j];
if (i % p[j] == 0) {
break;
}
}
int o = 0;
for (int j = i;j > 1;j /= a[j]) {
b[o++] = a[j];
}
sort(b,b+o);
o = unique(b,b+o) - b;
for (int j = 0;j<o;j++) {
add(i/b[j]);
//cout << "debug " << i / b[j] << endl;
}
for (;u<tt && x[u].r <= i;u++) {
if (x[u].l >= x[u].r) ans[x[u].id] = 0;
else {
ans[x[u].id] = sum(x[u].r) - sum(x[u].l - 1);
}
}
if (u==tt) break;
}
}
bool cmp(point a,point b) {
return a.r < b.r;
}
int main(){
#ifdef ZHOUZHENTAO
freopen("test.in", "r", stdin);
#endif
while (~scanf("%d%d",&x[tt].l,&x[tt].r)){
x[tt].id = tt;
tt ++;
}
sort(x,x+tt,cmp);
init();
for (int i = 0;i < tt;i++) {
printf("%d\n",ans[i]);
}
return 0;
}
E - 小木乃伊到我家
最短路。一开始忘记优先队列是大的排在前面了,果然是智力有缺陷。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 4e5 + 10;
long long INF = 1e9;
int T;
int n, m;
int sz, h[maxn], nx[maxn], to[maxn];
long long val[maxn];
long long dis[maxn];
void add(int x, int y, long long z) {
to[sz] = y;
val[sz] = z;
nx[sz] = h[x];
h[x] = sz ++;
}
int main() {
INF = INF * INF;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) {
h[i] = -1;
dis[i] = INF;
}
for(int i = 1; i <= m; i ++) {
int u, v;
long long w;
scanf("%d%d%lld", &u, &v, &w);
add(u, v, w);
add(v, u, w);
}
priority_queue<pair<long long, int> > q;
q.push(make_pair(0, 1));
dis[1] = 0;
while(!q.empty()) {
pair<long long, int> pi = q.top();
q.pop();
if(dis[pi.second] < -pi.first) continue;
for(int i = h[pi.second]; i!=-1;i=nx[i]) {
if(dis[pi.second] + val[i] < dis[to[i]]) {
dis[to[i]] = dis[pi.second] + val[i];
q.push(make_pair(-dis[to[i]], to[i]));
}
}
}
if(dis[n] == INF) {
printf("qwb baka\n");
} else {
printf("%lld\n", dis[n]);
}
return 0;
}
F - 箱庭的股市
仔细观察一下可以发现就是求杨辉三角某一行的前缀和。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
int m,x,y,p;
int f[N], g[N], inv[N];
int main(){
f[0] = g[0] = f[1] = g[1] = inv[1] = 1;
for (int i = 2;i<N;i++) {
f[i] = 1LL * f[i-1] * i % mod;
inv[i] = 1LL * inv[mod % i] * (mod - mod /i) % mod;
g[i] = 1LL * g[i-1] * inv[i] % mod;
}
while (~scanf("%d%d%d%d",&m,&x,&y,&p)) {
int ans = 0;
if (x==1||y==0) {
printf("%d\n", p);
continue;
}
x = x - 1;
for (int i = 0;i<= min(y,x);i++) {
ans = (ans + 1LL * f[x] * g[i] % mod * g[x-i] % mod) % mod;
}
ans = 1LL * p * ans % mod;
printf("%d\n",ans);
}
}
G - 逃离迷宫
吐槽:题目里说:“现在你需要花费最少的时间拿到钥匙然后从迷宫的出口出去”。我一开始理解为首先是要求到钥匙的时间最少,然后从最少时间的钥匙那里挑一个走到出口,结果答案错误,后来改成整体最少时间就 AC 了。这个我感觉确实会引起歧义啊。
#include <bits/stdc++.h>
using namespace std;
int INF = 10000000;
const int maxn = 550;
int T;
int n, m;
char s[maxn][maxn];
int sx, sy;
int ex, ey;
int dis[2][maxn][maxn];
int dir[4][2] = {
{-1, 0},
{1, 0},
{0, -1},
{0, 1}
};
int out(int x, int y) {
if(x < 0 || x >= n) return 1;
if(y < 0 || y >= m) return 1;
return 0;
}
void bfs(int flag) {
queue<int> q;
if(flag == 0) {
dis[flag][sx][sy] = 0;
q.push(sx * m + sy);
} else {
dis[flag][ex][ey] = 0;
q.push(ex * m + ey);
}
while(!q.empty()) {
int st = q.front();
q.pop();
int nx = st / m;
int ny = st % m;
for(int i = 0; i < 4; i ++) {
int tx = nx + dir[i][0];
int ty = ny + dir[i][1];
if(out(tx, ty)) continue;
if(s[tx][ty] == '#') continue;
if(flag == 0 && s[tx][ty] == 'E') continue;
if(dis[flag][nx][ny] + 1 < dis[flag][tx][ty]) {
dis[flag][tx][ty] = dis[flag][nx][ny] + 1;
q.push(tx * m + ty);
}
}
}
}
int main() {
scanf("%d", &T);
while(T --) {
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i ++) {
scanf("%s", s[i]);
}
for(int i = 0; i < n; i ++) {
for(int j = 0; j < m; j ++) {
if(s[i][j] == 'P') sx = i, sy = j;
else if(s[i][j] == 'E') ex = i, ey = j;
dis[0][i][j] = dis[1][i][j] = INF;
}
}
bfs(0);
bfs(1);
int ans = INF;
for(int i = 0; i < n; i ++) {
for(int j = 0; j < m; j ++) {
if(s[i][j] != 'K') continue;
if(dis[0][i][j] == INF) continue;
if(dis[1][i][j] == INF) continue;
ans = min(ans, dis[0][i][j] + dis[1][i][j]);
}
}
if(ans == INF) {
printf("No solution\n");
continue;
}
printf("%d\n", ans);
}
return 0;
}
H - 数学考试
枚举分割线,然后左边最大值和右边最大值加一加更新答案。左右两边最大值 dp 一下就能算出来了。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
long long INF = 1e9;
int T, n, k;
long long a[maxn];
long long L[maxn], R[maxn];
int main() {
INF = INF * INF;
scanf("%d", &T);
while(T --) {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i ++) {
scanf("%lld", &a[i]);
}
long long sum = 0;
for(int i = 1; i <= k; i ++) {
sum = sum + a[i];
}
L[k] = sum;
for(int i = k + 1; i <= n; i ++) {
sum = sum - a[i - k] + a[i];
L[i] = max(L[i - 1], sum);
// cout << sum << endl;
}
sum = 0;
for(int i = n; i >= n - k + 1; i --) {
sum = sum + a[i];
}
R[n - k + 1] = sum;
for(int i = n - k; i >= 1; i --) {
sum = sum - a[i + k] + a[i];
R[i] = max(R[i + 1], sum);
// cout << sum << endl;
}
long long ans = L[k] + R[k + 1];
for(int i = k; i + k <= n; i ++) {
// [, i], [i + 1, i + k]
ans = max(ans, L[i] + R[i + 1]);
// cout << L[i] << " " << R[i + 1] << endl;
}
printf("%lld\n", ans);
}
return 0;
}
I - 连续区间的最大公约数
以每一个位置 $i$ 作为区间右端点,左端点从 $[1, i]$ 移动的过程中,区间 gcd 的种类不会超过 $log(n)$ 种,我们可以把这些处理出来,记录成四元组 $\left( { L }_{ 1 },L_{ 2 },R,g \right) $,表示区间左端点在 $[L_1,L_2]$,右端点在 $R$ 的所有区间,最大公约数均为 $g$。
所有的四元组已经 cover 了所有的子区间。那么对于每一次的询问 $(L, R, g)$,我们只需在和这一次询问拥有相同 $g$ 的四元组中寻找答案,去计算每一个四元组做出的贡献。
例如询问为 $(4, 7, 3)$,四元组 $(1, 3, 3, 3)$ 的贡献为 0,虽然 $g$ 均为 3,但是无相交部分。四元组 $(2, 5, 6, 3)$ 的贡献为 2。虽然以 6 为结尾的有 4 个区间 $g$ 为 3,但是只有 2 个在询问的区间内。
一种效率较高的做法如下:
把询问离线,询问中 $g$ 相同的归为一类,去 gcd 为 $g$ 的四元组中计算答案。也就是按 $g$ 分类来计算。
下面只考虑相同 $g$ 情况下的询问和四元组之间如何计算答案。
对于一次询问 $(L, R, g)$,就是求 $R$ 小于等于询问的 $R$ 的四元组 $[L_1,L_2]$ 和 $[L, R]$ 交集长度之和。每次暴力求的话,复杂度为 $O(n^2)$,事实上可以按 $R$ 排序,然后利用线段树区间修改、求区间和来搞。
/*******************************
Judge Result : AC
*******************************/
#include <bits/stdc++.h>
#define rep(i,j,k) for (int i = j; i <= k; i++)
#define per(i,j,k) for (int i = j; i >= k; i--)
#define loop(i,j,k) for (int i = j;i != -1; i = k[i])
#define lson x << 1, l, mid
#define rson x << 1 | 1, mid + 1, r
#define ff first
#define ss second
#define mp(i,j) make_pair(i,j)
#define pb push_back
#define pii pair<int,LL>
#define in(x) scanf("%d", &x);
using namespace std;
typedef long long LL;
const int low(int x) { return x&-x; }
const double eps = 1e-4;
const int INF = 0x7FFFFFFF;
const int N = 4e5 + 10;
int T, n, m, l, r;
int g[N],d[N];
long long s[N], k[N];
int gcd(int x, int y) {
return x%y ? gcd(y, x%y) : y;
}
void build(int x,int l,int r) {
if (l == r) {
scanf("%d",&g[x]);
d[r] = g[x];
}
else {
int mid = l + r >> 1;
build(lson);
build(rson);
g[x] = gcd(g[x<<1], g[x<<1|1]);
}
}
int query(int x,int l,int r,int ll,int rr) {
if (ll<=l && r<=rr) return g[x];
int mid = l + r>>1;
if (rr <= mid) return query(lson,ll,rr);
else if (ll > mid) return query(rson,ll,rr);
else return gcd(query(lson,ll,rr),query(rson,ll,rr));
}
void pushUp(int rt) {
s[rt] = s[2 * rt] + s[2 * rt + 1];
}
void pushDown(int rt, int l, int r) {
if(k[rt] == 0) return;
int mid = (l + r) / 2;
s[2 * rt] += (k[rt] * (mid - l + 1));
s[2 * rt + 1] += (k[rt] * (r - mid));
k[2 * rt] += k[rt];
k[2 * rt + 1] += k[rt];
k[rt] = 0;
}
void update(int L, int R, long long val, int l, int r, int rt) {
if(L <= l && r <= R) {
s[rt] += (val * (r - l + 1));
k[rt] += val;
return;
}
int mid = (l + r) / 2;
pushDown(rt, l, r);
if(L <= mid) update(L, R, val, l, mid, 2 * rt);
if(R > mid) update(L, R, val, mid + 1, r, 2 * rt + 1);
pushUp(rt);
}
long long get(int L, int R, int l, int r, int rt) {
if(L <= l && r <= R) return s[rt];
pushDown(rt, l, r);
int mid = (l + r) / 2;
long long left = 0, right = 0;
if(L <= mid) left = get(L, R, l, mid, 2 * rt);
if(R > mid) right = get(L, R, mid + 1, r, 2 * rt + 1);
pushUp(rt);
return left + right;
}
struct point{
int l,r,g, id;
}c[N];
struct point2 {
int l1, l2, r, g;
}h[N];
int szh;
long long ans[N];
bool cmpc(const point& a, const point& b) {
if(a.g != b.g) return a.g < b.g;
return a.r < b.r;
}
bool cmph(const point2& a, const point2& b) {
if(a.g != b.g) return a.g < b.g;
return a.r < b.r;
}
bool cmpid(const point& a, const point& b ) {
return a.id < b.id;
}
int main() {
#ifdef ZHOUZHENTAO
freopen("test.in", "r", stdin);
#endif
scanf("%d", &T);
int cas = 0;
while (T--) {
scanf("%d", &n);
build(1, 1, n);
scanf("%d", &m);
printf("Case #%d:\n", ++cas);
for (int i = 0;i< m ;i++) {
scanf("%d%d", &c[i].l, &c[i].r);
c[i].g = query(1, 1, n, c[i].l, c[i].r);
c[i].id = i;
}
szh = 0;
stack<pii> a, b;
rep(i, 1, n) {
a.push(mp(d[i], 1));
while (!a.empty()) b.push(mp(gcd(a.top().ff, d[i]), a.top().ss)), a.pop();
while (!b.empty()) {
pii q = b.top(); b.pop();
if (!a.empty() && a.top().ff == q.ff) q.ss += a.top().ss, a.pop();
a.push(q);
}
int L = i;
while (!a.empty()) {
pii q = a.top(); a.pop();
int LL = L - q.second + 1;
h[szh].l1 = LL;
h[szh].l2 = L;
h[szh].r = i;
h[szh].g = q.first;
szh ++;
L = LL - 1;
b.push(q);
}
while (!b.empty()) {
a.push(b.top()); b.pop();
}
}
sort(c, c + m, cmpc);
sort(h, h + szh, cmph);
/*
for(int i = 0; i < szh; i ++) {
cout << h[i].l1 << " " << h[i].l2 << " " << h[i].r << " " << h[i].g << endl;
}
for(int i = 0; i < m; i ++) {
cout << c[i].l << " " << c[i].r << " " << c[i].g << endl;
}
*/
for(int i = 0; i < m; i ++) {
ans[i] = 0;
}
for(int i = 0, j = 0; i < szh; i = j + 1, j = i) {
while(j < szh - 1 && h[j].g == h[j + 1].g) j ++;
// [i, j]
int L = 0, R = m - 1, pos1 = -1, pos2 = -1;
while(L <= R) {
int mid = (L + R) / 2;
if(c[mid].g < h[i].g) L = mid + 1;
else if(c[mid].g > h[i].g) R = mid - 1;
else pos1 = mid, R = mid - 1;
}
if(pos1 == -1) continue;
L = 0, R = m - 1;
while(L <= R) {
int mid = (L + R) / 2;
if(c[mid].g < h[i].g) L = mid + 1;
else if(c[mid].g > h[i].g) R = mid - 1;
else pos2 = mid, L = mid + 1;
}
//[pos1, pos2]
/*
printf("[%d, %d] [%d, %d]\n", i, j, pos1, pos2);
*/
int y = i;
for(int k = pos1; k <= pos2; k ++) {
while(y <= j && h[y].r <= c[k].r) {
update(h[y].l1, h[y].l2, 1LL, 1, n, 1);
y ++;
}
ans[c[k].id] = get(c[k].l, c[k].r, 1, n, 1);
}
for(int k = i; k < y; k ++) {
update(h[k].l1, h[k].l2, -1LL, 1, n, 1);
}
}
sort(c, c + m, cmpid);
for(int i = 0; i < m; i ++) {
printf("%d ", c[i].g);
printf("%lld\n", ans[i]);
}
}
return 0;
}
J - 杯子
这题和这题完全一样。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL p = 1e9 + 7;
const int maxn = 3e6 + 10;
LL f[maxn];
//******************************
//返回d=gcd(a,b);和对应于等式ax+by=d中的x,y
long long extend_gcd(long long a,long long b,long long &x,long long &y)
{
if(a==0&&b==0) return -1;//无最大公约数
if(b==0){x=1;y=0;return a;}
long long d=extend_gcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
//*********求逆元素*******************
//ax = 1(mod n)
long long mod_reverse(long long a,long long n)
{
long long x,y;
long long d=extend_gcd(a,n,x,y);
if(d==1) return (x%n+n)%n;
else return -1;
}
LL C(LL n, LL m) {
long long A = f[n];
long long B = f[n - m] * f[m] % p;
long long C = mod_reverse(B, p);
return A * C % p;
}
LL work(LL n, LL m) {
if(n == m) return 1;
return (C(n * 2 - m - 1, n - m)
- C(n * 2 - m - 1, n - m - 1)
+ p) % p;
}
int main() {
f[0] = 1;
for(long long i = 1; i < maxn; i ++) {
f[i] = (f[i - 1] * i) % p;
}
int T;
scanf("%d", &T);
while(T --) {
LL n, m, k;
scanf("%lld%lld%lld", &n, &m, &k);
if(m > n || m < k) {
printf("0\n");
continue;
}
LL A = work(m, k);
LL B = work(n + k + 1 - m, k + 1);
LL ans = A * B % p;
printf("%lld\n", ans);
}
return 0;
}
/*
A:
---+-----------------------------------
m\k| 1 2 3 4 5 6 7
---+-----------------------------------
1 | 1
2 | 1 1
3 | 2 2 1
4 | 5 5 3 1
5 | 14 14 9 4 1
6 | 42 42 28 14 5 1
7 | 132 132 90 48 20 6 1
B:
n = 1
---+-----------------------------------
m\k| 1 2 3 4 5 6 7
---+-----------------------------------
1 | 1
2 |
3 |
4 |
5 |
6 |
7 |
n = 2
---+-----------------------------------
m\k| 1 2 3 4 5 6 7
---+-----------------------------------
1 | 2
2 | 1 1
3 |
4 |
5 |
6 |
7 |
n = 3
---+-----------------------------------
m\k| 1 2 3 4 5 6 7
---+-----------------------------------
1 | 5
2 | 2 3
3 | 1 1 1
4 |
5 |
6 |
7 |
n = 4
---+-----------------------------------
m\k| 1 2 3 4 5 6 7
---+-----------------------------------
1 | 14
2 | 5 9
3 | 2 3 4
4 | 1 1 1 1
5 |
6 |
7 |
n = 5
---+-----------------------------------
m\k| 1 2 3 4 5 6 7
---+-----------------------------------
1 | 42
2 | 14 28
3 | 5 9 14
4 | 2 3 4 5
5 | 1 1 1 1 1
6 |
7 |
*/
K - zzq的离散数学教室2
这题看上去好像和 2017 ICPC 南宁 M 题一样,都是求最长反链长度。最长反链长度 = 最小链覆盖数,原题在 BZOJ 1143。两种做法:第一种做法传递闭包搞,第二种做法网络流上对流量做一些改造。第二种能适应这题的数据规模。
L - 仓鼠养殖计划
队友写的,我题意都不知道。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int T,a,b,n, c[N];
bool check() {
for (int i = 1;i<=n;i++) {
while (c[i]) {
if (a+b==0) break;
if (a==0) {
c[i] = max(0,c[i]-2);
b--;
}
else if (b==0) {
c[i]--; a--;
}
else if (c[i]>1) {
c[i]-=2; b--;
}
else {
c[i]--; a--;
}
}
if (c[i]) return false;
}
return true;
}
int main(){
for (scanf("%d",&T);T--;) {
scanf("%d%d%d",&a,&b,&n);
for (int i = 1;i<=n;i++) {
scanf("%d",&c[i]);
}
puts(check()?"Yes":"No");
}
return 0;
}