2020牛客NOIP赛前集训营-提高组(第五场)
A 三元组计数
分析
我们对于三元组的考虑,有一个套路,枚举二元组然后再考虑贡献。那么我们先找到有多少个二元组 满足 。那么这个根据调和级数可以得到 。那么我们就可以直接枚举每个元素的倍数就好了。最后再考虑每个二元组的贡献就好了。总的复杂度为 。
代码
#include<bits/stdc++.h> using namespace std; #define ll long long const int N = 1e6 + 100; int read() { int x = 0,f = 0;char ch = getchar(); while(!isdigit(ch)) {if(ch=='-')f=1;ch=getchar();} while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} return f?-x:x; } int n; ll ans = 0,A[N]; int main() { n = read(); for(int i = 1;i <= n;i++) { for(int j = i * 2;j <= n;j += i) { A[j]++; } } for(int i = 2;i <= n;i++) { for(int j = i * 2;j <= n;j += i) { ans += A[i]; } } cout << ans << endl; return 0; }
B K匹配
分析
仍然是个套路题。我们考虑所有子串,可以通过考虑所有前缀的后缀得到。那么我们先通过 求出所有的完美匹配点。那么考虑一个位置 时,当这个节点作为后缀的结束点, 表示离 最近的完美匹配点,那么开头的可取范围就应该是 。那么 扫一次就可以了。那么总的复杂度为 。
代码
#include<bits/stdc++.h> using namespace std; #define ll long long const int N = 1e7 + 100; int read() { int x = 0,f = 0;char ch = getchar(); while(!isdigit(ch)) {if(ch=='-')f=1;ch=getchar();} while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} return f?-x:x; } int n,m,nxt[N]; char s[N],t[N]; bool vis[N]; ll ans = 0; int main() { scanf("%d%d%s%s",&n,&m,s + 1,t + 1); for(int i = 2,j = 0;i <= m;i++) { while(j && t[j + 1] != t[i]) j = nxt[j]; if(t[j + 1] == t[i]) j++; nxt[i] = j; } for(int i = 1,j = 0;i <= n;i++) { while(j && t[j + 1] != s[i]) j = nxt[j]; if(t[j + 1] == s[i]) j++; if(j == m) {vis[i] = 1;j = nxt[j];} } for(int i = 1,last = 0;i <= n;i++) { if(vis[i]) last = i - m + 1; ans += last; } cout << ans << endl; }
C 经典字符串问题
分析
经典主席树问题。静态区间第 小问题。我们可以先对 数字构成的字符串排序。这个每个数字 便对应了一个 ,因为这里的字符串长度为 的,所以可以直接快排,时间复杂度为 。那么我们,之后的问题就是一个主席树的模板了,注意代码中 和 的对应关系,总的复杂度为 。
代码
#include<bits/stdc++.h> using namespace std; #define ll long long const int N = 1e5 + 100; int read() { int x = 0,f = 0;char ch = getchar(); while(!isdigit(ch)) {if(ch=='-')f=1;ch=getchar();} while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} return f?-x:x; } int lc[N * 40],rc[N * 40],cnt[N * 40],rt[N],n,Q,id[N],size = 0; struct Node {int pos;char ch[22];}s[N]; bool cmp(Node a,Node b) { int len = max(strlen(b.ch+1),strlen(a.ch+1)); for(int i = 1;i <= len;i++) if(a.ch[i] != b.ch[i]) return a.ch[i] < b.ch[i]; } void build(int &u,int l,int r) { if(!u) u = ++size; int mid = l + r >> 1; if(l == r) return; build(lc[u],l,mid);build(rc[u],mid+1,r); } void update(int &u,int la,int l,int r,int pos) { u = ++size;lc[u] = lc[la];rc[u] = rc[la];cnt[u] = cnt[la] + 1; if(l == r) return; int mid = l + r >> 1; if(pos <= mid) update(lc[u],lc[la],l,mid,pos); else update(rc[u],rc[la],mid+1,r,pos); } int ask(int a,int b,int l,int r,int k) { if(l == r) return l; int res = cnt[lc[b]] - cnt[lc[a]],mid = l + r >> 1; if(res >= k) return ask(lc[a],lc[b],l,mid,k); else return ask(rc[a],rc[b],mid+1,r,k-res); } int main() { n = read();Q = read(); for(int i = 1;i <= n;i++) { scanf("%s",s[i].ch + 1);s[i].pos = i; } sort(s+1,s+1+n,cmp); for(int i = 1;i <= n;i++) id[s[i].pos] = i; build(rt[0],1,n); for(int i = 1;i <= n;i++) update(rt[i],rt[i-1],1,n,id[i]); while(Q--) { int l = read(),r = read(),k = read();l--; if(k > cnt[rt[r]] - cnt[rt[l]]) printf("-1\n"); else { int Id = ask(rt[l],rt[r],1,n,k); printf("%s\n",s[Id].ch + 1); } } return 0; }
D 圆与圆之间的距离是不能一概而论的
- 先说明这个做法的时间复杂度并不正确。但是在这个数据下可过,应该一个菊花或者链就死了。这里只是提供一种思路。
分析
我们考虑到因为圆只有包含和相离的关系,所以集合与集合的关系构成了一些森林。我们先假想一个大小无限的的圆,包含了所有圆,那么就构成了一棵树。考虑树上的距离,查询 保证 。如果 那么答案为 否则 。这个可以通过画图手模一下就知道了,因为答案为 中除开 的节点个数。那么我们现在的问题就是如何建树了,由于本人太菜,没有什么复杂度是对的建树方法,这里大家就当看个笑话。因为考虑到如果 的圆包含 ,那么 。然后在建树时递归一下就好了。复杂度 。
代码
#include<bits/stdc++.h> using namespace std; #define ll long long const int N = 1e5 + 100; int read() { int x = 0,f = 0;char ch = getchar(); while(!isdigit(ch)) {if(ch=='-')f=1;ch=getchar();} while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} return f?-x:x; } struct Node{ int x,y,r,id; }s[N]; bool cmp(Node a,Node b) {return a.r > b.r;} int n,dep[N],fa[N][24],Log[N]; vector<int> G[N]; vector<int> S[N]; void dfs(int x,int d) { dep[x] = d; for(int i = 1;i <= Log[dep[x]];i++) fa[x][i] = fa[fa[x][i-1]][i-1]; for(auto y : G[x]) dfs(y,d + 1); } int lca(int a,int b) { if(dep[a] < dep[b]) swap(a,b); int k = dep[a] - dep[b]; for(int i = Log[k];~i;i--) if((k >> i) & 1) a = fa[a][i]; if(a == b) return a; for(int i = Log[dep[a]];~i;i--) if(fa[a][i] ^ fa[b][i]) a = fa[a][i],b = fa[b][i]; return fa[a][0]; } bool in(int a,int b) { return 1ll*(s[a].r + s[b].r) * (s[a].r + s[b].r) > 1ll*(s[a].x - s[b].x) * (s[a].x - s[b].x) + 1ll * (s[a].y-s[b].y) * (s[a].y-s[b].y); } void build(int u,int v) { for(auto to : S[u]) if(in(to,v)) {build(to,v);return;} G[s[u].id].push_back(s[v].id);fa[s[v].id][0] = s[u].id; S[u].push_back(v); } int main() { n = read(); for(int i = 2;i <= n + 1;i++) Log[i] = Log[i >> 1] + 1; for(int i = 1;i <= n;i++) { s[i].x = read();s[i].y = read();s[i].r = read();s[i].id = i; } sort(s + 1,s + 1 + n,cmp);s[n + 1].id = n + 1; for(int i = 1;i <= n;i++) build(n + 1,i); dfs(n + 1,1); int Q = read(); while(Q--) { int x = read(),y = read(); int t = lca(x,y); if(dep[x] > dep[y]) swap(x,y); int ans = dep[x] + dep[y] - dep[t] * 2; if(t != x) ans--;ans--; printf("%d\n",ans); } }