2022NOIP A层联测33
A. GCD
不会分析复杂度 + 一个神奇错误 寄了
首先大部分人做法都会先枚举 , 然后一个调和级数开始了
然后发现难以处理询问
有人的做法是设定一个阈值,分成爆扫询问和爆扫合并的点对两部分,可以分析出是 级别的
我写的垃圾题解做法,不开火车头会在上寄掉
是个神奇的东西,这题 会寄,但是 就能过
于是把 变成 ,维护联通点集,然后枚举有影响的点,跟他的询问 一下,暴力枚举即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 30055;
int n, m, q;
struct edge{int u, v;}e[maxn * 10];
map<int, int>id[maxn];
bitset<maxn>que[maxn];
int ans[maxn * 10];
struct DSU{
int f[maxn];
bitset<maxn>ele[maxn];
bitset<maxn>now, ls;
void init(){for(int i = 1; i <= n; ++i)f[i] = i, ele[i].set(i);}
int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
void merge(int u, int v){
u = fa(u), v = fa(v);
if(u == v)return;
now.set(u); now.set(v);
ele[u] |= ele[v]; f[v] = u;
}
void sol(int val){
if(now.none())return;
//printf("val = %d\n",val);
for(int i = now._Find_first(); i <= n; i = now._Find_next(i)){
ls = ele[fa(i)] & que[i];
for(int j = ls._Find_first(); j <= n; j = ls._Find_next(j)){
ans[id[i][j]] = val;
//printf("%d %d\n",i, j);
}
}
for(int i = now._Find_first(); i <= n; i = now._Find_next(i)){
ele[i].reset(); ele[i].set(i); f[i] = i;
}
now.reset();
}
}S;
int main(){
freopen("gcd.in","r",stdin);
freopen("gcd.out","w",stdout);
n = read(), m = read(), q = read();
for(int i = 1; i <= m; ++i){
int u = read(), v = read(), w = read();
e[w] = {u, v};
}
for(int i = 1; i <= q; ++i){
int u = read(), v = read();
if(u > v)swap(u, v);
que[u].set(v);
ans[id[u][v]] = -i;
id[u][v] = i;
}
S.init();
for(int i = 1; i <= 100000; ++i){
for(int j = i; j <= 100000; j += i)
if(e[j].u)S.merge(e[j].u, e[j].v);
S.sol(i);
}
for(int i = q; i >= 1; --i)if(ans[i] < 0)ans[i] = ans[-ans[i]];
for(int i = 1; i <= q; ++i)printf("%d\n",ans[i] ? ans[i] : -1);
return 0;
}
B. 简单题
为什么这题有模数?
为什么这题 会 成暴力分?
为什么我还是这么菜?
势能分析,一个点最多被修 次,暴力递归到叶子修改,但是对于区间全修为 的可以直接计算
复杂度
不就差了 常数吗?
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 1000005;
const int mod = 998244353;
int n, m;
int prime[maxn], cnt, phi[maxn];
bool flag[maxn];
void init(int mx){
phi[1] = 1;
for(int i = 2; i <= mx; ++i){
if(!flag[i])prime[++cnt] = i, phi[i] = i - 1;
for(int j = 1; j <= cnt && i * prime[j] <= mx; ++j){
flag[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]];
}
}
}
int a[maxn], b[maxn];
pll operator + (const pll &x, const pll &y){return pll(x.first + y.first, x.second + y.second);}
struct seg{
struct node{ll va, vb; int tag, mx, siz;}t[maxn];
void push_up(int x){
t[x].va = t[x << 1].va + t[x << 1 | 1].va;
t[x].vb = t[x << 1].vb + t[x << 1 | 1].vb;
t[x].mx = max(t[x << 1].mx, t[x << 1 | 1].mx);
}
void upd(int x, int tim){t[x].va += 1ll * t[x].siz * tim; t[x].tag += tim;}
void push_down(int x){upd(x << 1, t[x].tag); upd(x << 1 | 1, t[x].tag); t[x].tag = 0;}
void modify(int x, int l, int r, int L, int R){
if(L <= l && r <= R){
if(l == r){
t[x].va = t[x].va + t[x].vb;
t[x].vb = t[x].mx = phi[t[x].vb];
return;
}
if(t[x].mx == 1)return upd(x, 1);
}
int mid = (l + r) >> 1;
if(t[x].tag)push_down(x);
if(L <= mid)modify(x << 1, l, mid, L, R);
if(R > mid)modify(x << 1 | 1, mid + 1, r, L, R);
push_up(x);
}
void built(int x, int l, int r){
t[x].siz = r - l + 1;
if(l == r){t[x].va = a[l]; t[x].vb = t[x].mx = b[l]; return;}
int mid = (l + r) >> 1;
built(x << 1, l, mid);
built(x << 1 | 1, mid + 1, r);
push_up(x);
}
pll query(int x, int l, int r, int L, int R){
if(L <= l && r <= R)return pll(t[x].va, t[x].vb);
int mid = (l + r) >> 1; pll ans = pll(0, 0);
if(t[x].tag)push_down(x);
if(L <= mid)ans = ans + query(x << 1, l, mid, L, R);
if(R > mid)ans = ans + query(x << 1 | 1, mid + 1, r, L, R);
return ans;
}
}t;
int main(){
freopen("simple.in","r",stdin);
freopen("simple.out","w",stdout);
init(1000000);
n = read(), m = read();
for(int i = 1; i <= n; ++i)a[i] = read(), b[i] = read();
t.built(1, 1, n);
for(int i = 1; i <= m; ++i){
int opt = read(), l = read(), r = read();
if(opt & 1)t.modify(1, 1, n, l, r);
else{
pll ans = t.query(1, 1, n, l, r);
printf("%d %d\n",ans.first % mod, ans.second % mod);
}
}
return 0;
}
C. 建筑
于是 能过
然后就是如何 处理序列上的问题,每次处理以 为右界的区间,然后用单调栈维护一下即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 100005;
void ckmx(int &x, int y){if(x < y)x = y;}
int n, m;
vector<int>mp[maxn];
int mx[maxn];
ll sval[maxn];
int sta[maxn], top;
ll ans;
ll calc(ll x, ll y){return (x + y) * (y - x + 1) / 2;}
void sol(ll len){
ll now = 0, sum = 0, smax = 0; sta[top = 0] = -1;
//for(int i = 0; i < m; ++i)printf("%d ",mx[i]);printf("\n");
for(int i = 0; i < m; ++i){
sum += 1ll *sval[i] * (i + 1);
while(top && mx[sta[top]] < mx[i]){
smax -= 1ll * mx[sta[top]] * (sta[top] - sta[top - 1]);
now -= 1ll * mx[sta[top]] * calc(i - 1 - sta[top] + 1, i - 1 - sta[top - 1]);
--top;
}
now += smax;
smax += 1ll * mx[i] * (i - sta[top]);
now += 1ll * mx[i] * calc(1, i - sta[top]);
sta[++top] = i;
ans += 1ll * len * now - sum;
// printf("len = %d now = %d sum = %d ans = %d smax = %d\n",len, now, sum, ans, smax);
}
}
int main(){
freopen("building.in","r",stdin);
freopen("building.out","w",stdout);
n = read(), m = read();
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= m; ++j)if(n < m){
mp[i - 1].push_back(read());
}else{
mp[j - 1].push_back(read());
}
}
if(n > m)swap(n, m);
for(int i = 0; i < n; ++i){
for(int j = 0; j < m; ++j)mx[j] = 0, sval[j] = 0;
for(int j = i; j < n; ++j){
for(int k = 0; k < m; ++k)ckmx(mx[k], mp[j][k]), sval[k] += mp[j][k];
sol(j - i + 1);
}
}
printf("%lld\n",ans);
return 0;
}
D. 树上前缀和
暴力
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 100005;
int n, m, val[maxn], sval[maxn];
vector<int>g[maxn];
int dep[maxn], son[maxn], fa[maxn], siz[maxn], top[maxn];
void dfs1(int x){
siz[x] = 1; sval[x] += val[x];
for(int v : g[x])if(v != fa[x]){
fa[v] = x; dep[v] = dep[x] + 1; sval[v] = sval[x];
dfs1(v); siz[x] += siz[v];
son[x] = siz[son[x]] > siz[v] ? son[x] : v;
}
}
void dfs2(int x, int tp){
top[x] = tp; if(son[x])dfs2(son[x], tp);
for(int v : g[x])if(v != fa[x] && v != son[x])dfs2(v, v);
}
int lca(int u, int v){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]])swap(u, v);
u = fa[top[u]];
}
return dep[u] < dep[v] ? u : v;
}
int main(){
freopen("shuqianzhui.in","r",stdin);
freopen("shuqianzhui.out","w",stdout);
n = read(), m = read();
for(int i = 1; i <= n; ++i)val[i] = read();
for(int i = 1, u, v; i < n; ++i){u = read(), v = read(); g[u].push_back(v); g[v].push_back(u);}
dfs1(1); dfs2(1, 1);
for(int i = 1; i <= m; ++i){
int u = read(), v = read(), lc;
lc = lca(u, v); printf("%d\n",sval[u] + sval[v] - sval[lc] - sval[fa[lc]]);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】