牛客小白月赛12
这真的是小白月赛么……
说好的“题目难度在\(CF\; DIV2\;A\)~\(C\)”呢?
\(D,E\)两题为数学题,所以不想整(并没有歧视数学的意思)
A.华华听月月唱歌
贪心区间覆盖裸题,但细节需要注意
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
LL read() {
LL k = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9')
k = k * 10 + c - 48, c = getchar();
return k * f;
}
struct zzz {
int f, t;
}xd[100010];
bool cmp(zzz x, zzz y) {
if(x.f != y.f) return x.f < y.f;
else return x.t > y.t;
}
int main() {
int n = read(), tot = read();
for(int i = 1; i <= tot; ++i)
xd[i].f = read(), xd[i].t = read();
sort(xd+1, xd+tot+1, cmp);
int t = 0, ans = 0, pos = 1;
while(t < n) {
++ans;
int flag = t;
for(; xd[pos].f <= flag+1 && pos <= tot; ++pos)
t = max(t, xd[pos].t);
if(t == flag && flag < n) {
cout << -1; exit(0);
}
}
cout << ans;
return 0;
}
B.华华教月月做数学
快速幂+龟速乘
#include<iostream>
#include<cstdio>
#define LL long long
using namespace std;
LL mul(LL b, LL p, LL k) {
LL ans = 0;
for(; p; p >>= 1) {
if(p & 1)
ans = (ans + b) % k;
b = (b + b) % k;
}
return ans % k;
}
LL pow(LL b, LL p, LL k) {
LL ans = 1;
for(; p; p >>= 1) {
if(p & 1)
ans = mul(ans, b, k);
b = mul(b, b, k);
}
return ans % k;
}
LL read() {
LL k = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9')
k = k * 10 + c - 48, c = getchar();
return k * f;
}
int main() {
int t = read();
while(t--) {
LL b = read(), p = read(), k = read();
cout << pow(b, p, k) << endl;
}
return 0;
}
还有好写的Python
t = input()
for i in range(0, (int)(t)) :
s = input().split()
print(pow(int(s[0]), int(s[1]) , int(s[2])))
E.华华给月月准备礼物
二分答案
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
LL read() {
LL k = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9')
k = k * 10 + c - 48, c = getchar();
return k * f;
}
int a[200010], l = 1, r;
int n, m;
bool judge(int x) {
int ans = 0;
for(int i = 1; i <= n; ++i)
ans += a[i]/x;
if(ans < m) return 0;
else return 1;
}
int main() {
n = read(), m = read();
for(int i = 1; i <= n; ++i)
a[i] = read(), r = max(a[i], r)+1;
while(l < r) {
int mid = (l+r) >> 1;
if(judge(mid)) l = mid+1;
else r = mid;
}
cout << l-1;
return 0;
}
F.华华开始学信息学
很神仙的一道题,之前从来没见过这种思想
如果暴力使用树状数组来维护,每次修改的时间复杂度为\(O(\frac{n}{x}\log n)\),\(x\)为模数,查询时间复杂度为\(O(n \log n)\)
显然可以发现当\(x\)越大,使用树状数组维护的效率越高,而\(x\)较小的时候使用树状数组是十分不理智的,此时可以使用一个桶来维护,令tong[i]
表示模数为\(i\)时加了多少
那\(x\)为多少时用树状数组或是桶呢?
显然当\(x\)为\(\sqrt{n}\)时效率最高,修改时为\(O(\sqrt{n} \log n)\),查询为\(O(\sqrt{n}+ \log n)\)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#define LL long long
#define lb (i & (-i))
using namespace std;
LL read() {
LL k = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9')
k = k * 10 + c - 48, c = getchar();
return k * f;
}
LL tree[100010 << 1], tong[100010];
int n, m;
void add(int pos, LL x) {
for(int i = pos; i <= n; i += lb) tree[i] += x;
}
LL sum(int pos) {
LL ans = 0;
for(int i = pos; i; i -= lb) ans += tree[i];
return ans;
}
int main() {
n = read(), m = read(); int maxx = sqrt(n);
for(int i = 1; i <= m; ++i) {
LL opt = read(), x = read(), y = read();
if(opt == 1) {
if(x > maxx)
for(int j = x; j <= n; j += x) add(j, y);
else tong[x] += y;
}
else {
LL ans = sum(y) - sum(x-1);
for(int j = 1; j <= maxx; ++j) ans += (y/j - (x-1)/j) * tong[j];
printf("%lld\n", ans);
}
}
return 0;
}
G.华华对月月的忠诚
虽然\(N\)很大,但答案和\(N\)没有关系,直接求\(a,b\)的\(gcd\)即可
H.华华和月月种树
一开始以为是树剖,事实证明我想多了(数据结构的受害者)
动态开点不好维护,我们可以离线操作
先在输入的时候跟着\(1\)操作加点,建出最终的树,然后求此树的\(DFS\)序和每个点的子树大小
这样就把\(2,3\)操作变为了区间加和单点查询,可以用查分树状数组来维护
而对于\(1\)操作,加入新的点时要将这个点上的信息清空
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#define lb (i & (-i))
#define LL long long
using namespace std;
int read() {
int k = 0; char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9')
k = k * 10 + c - 48, c = getchar();
return k;
}
struct zzz {
int t, nex;
}e[100010 << 1]; int head[100010], tot;
void add(int x, int y) {
e[++tot].t = y;
e[tot].nex = head[x];
head[x] = tot;
}
struct hhh {
int opt, x, y;
}que[400010]; int cnt;
int siz[100010], p[100010], num = 1;
void dfs(int pos, int fa) {
p[pos] = num;
for(int i = head[pos]; i; i = e[i].nex)
if(e[i].t != fa)
++num, dfs(e[i].t, pos), siz[pos] += siz[e[i].t];
siz[pos] += 1;
}
int m, n;
LL tree[100010 << 1];
void del(int x) {
for(int i = x; i <= n; i += lb) tree[i] = 0;
}
void update(int x, int k) {
for(int i = x; i <= n; i += lb) tree[i] += k;
}
LL sum(int x) {
LL ans = 0;
for(int i = x; i; i -= lb) ans += tree[i];
return ans;
}
int main() {
m = read(), n = 1;
for(int i = 1; i <= m; ++i) {
int x = read(); que[++cnt].opt = x;
if(x == 1) {
int y = read()+1; que[cnt].x = y;
add(++n, y), add(y, n);
}
if(x == 2)
que[cnt].x = read()+1, que[cnt].y = read();
if(x == 3)
que[cnt].x = read()+1;
}
dfs(1, 0); num = 1;
for(int i = 1; i <= cnt; ++i) {
if(que[i].opt == 1) {
int pos = p[++num];
int k = sum(pos); update(pos, -k), update(pos+1, k);
}
if(que[i].opt == 2) {
int pos = que[i].x, k = que[i].y;
update(p[pos], k); update(p[pos]+siz[pos], -k);
}
if(que[i].opt == 3) {
int pos = que[i].x;
printf("%lld\n", sum(p[pos]));
}
}
return 0;
}
I.华华和月月逛公园
\(Tarjan\)求割边板子题
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct zzz {
int t, nex;
}e[300010 << 1];
int head[100010], tot;
void add(int x, int y) {
e[++tot].t = y;
e[tot].nex = head[x];
head[x] = tot;
}
int dfn[100010], low[100010], deth, ans;
void tarjan(int pos, int fa) {
low[pos] = dfn[pos] = ++deth;
for(int i = head[pos]; i; i = e[i].nex) {
int to = e[i].t;
if(!dfn[to]) {
tarjan(to, pos);
low[pos] = min(low[pos], low[to]);
if(low[to] > dfn[pos]) ++ans;
}
else if(to != fa)
low[pos] = min(low[pos], dfn[to]);
}
}
int read() {
int k = 0; char c = getchar();
for (; c < '0' || c > '9';) c = getchar();
for (; c >= '0' && c <= '9'; c = getchar())
k = k * 10 + c - 48;
return k;
}
int main() {
int n = read(), m = read();
for(int i = 1; i <= m; ++i) {
int x = read(), y = read();
add(x, y), add(y, x);
}
for(int i = 1; i <= n; ++i)
if(!dfn[i]) tarjan(i, i);
cout << m - ans;
return 0;
}
J.月月查华华的手机
学了一个新东西:“序列自动机”
对于母串每一位都记一下下一次出现某个字符的位置。匹配的时候从第零位(虚根)开始,如果能一直匹配下去就是\(Yes\),否则就是\(No\)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#define LL long long
#define lb (i & (-i))
using namespace std;
LL read() {
LL k = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9')
k = k * 10 + c - 48, c = getchar();
return k * f;
}
int e[1000010][26];
char A[1000010];
int main() {
scanf("%s", A+1); int len = strlen(A+1);
for(int i = len-1; i >= 0; --i) {
for(int j = 0; j < 26; ++j) e[i][j] = e[i+1][j];
e[i][A[i+1]-'a'] = i+1;
}
int m = read();
for(int i = 1; i <= m; ++i) {
scanf("%s", A+1);
int nex = 0, pos = 1, len = strlen(A+1);
while(e[nex][A[pos]-'a'] && pos <= len+1)
nex = e[nex][A[pos]-'a'], ++pos;
if(pos <= len) printf("No\n");
else printf("Yes\n");
}
return 0;
}