2021“MINIEYE杯”中国大学生算法设计超级联赛(10)
2021/8/20 12:35:40
本文主要是介绍2021“MINIEYE杯”中国大学生算法设计超级联赛(10),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
2021“MINIEYE杯”中国大学生算法设计超级联赛(10)
庆祝暑期训练赛结束了
Pty loves lines
- 题意
\(n\)条直线,求直线相交的所有可能的交点数情况并输出。
- 思路
首先,每条直线最极端情况(所有直线不平行),那么就有\(\frac{(n * (n - 1))}{2}\)个交点。
之后,我们先管直线的平行情况。
- 所有直线相交,即0(所有\(n\)都存在的一种情况)
- \(n - 1\)条直线平行,即\(1 * (n - 1) = n - 1\),一种
- \(n - 2\)条直线平行,那么另外两条直线可以平行,也可以相交,那么答案为: \(2 * (n - 2) + 0\ or\ 2 * (n - 2) + 1\)两种。
- \(n - 3\)条直线平行,那么看另外三条直线\((0,2,3) + (3 * (n - 3))\)。
之后其实都差不多,那么我们也可以发现第四种情况的时候,看另外三条直线的情况,其实左边那部分就是\(n = 3\)时的情况,后面在推也一样,那么通过这个性质就可以求递推公式,然后用\(dp\)进行状态转移即可。
定义\(f[i][j]\)表示\(i\)条直线,\(j\)个交点的情况是否可以,(\(f[i][j]\)为1或0),那么\(f[i][j] = f[i - k][j + (k * (i - k))]\),就类似我上面的递推,然后\(O(n^4)\)打表程序就出来了0.0,其实我们发现这个性质后就应该想到可以用bitset加一层优化,然后通过打表可以发现,一大段数字在后面都是连续的不用管(最多在30000多那就一直连续了,这个连续的最大值也可以打表求出来),那么就可以直接暴力点不管那后面的,那么就可以随便跑了\(O(n * n * 32000 / w)\)。
code :
const int N = 710, M = 32000; bitset<M> f[N]; void init() { f[0][0] = 1; fep(i,1,700) { fep(k,1,i) { f[i] |= f[i - k] << (k * (i - k)); } } } void solve(){ int n; cin >> n; int limit = min(M - 1, n * (n - 1) / 2); bool flag = 0; for(int i = 0;i <= limit;i ++){ if(f[n][i]) { if(flag) cout << ' '; else flag = 1; cout << i; } } for(int i = limit + 1;i <= (n * (n - 1) / 2);i ++) { cout << ' ' << i; } cout << endl; }
Pty loves string
- 题意
给一个字符串\(S\),\(Q\)次询问,问\(S\)种长度为\(l\)的前缀 + 长度为\(r\)的后缀形成的字符串在\(S\)中出现的次数。
- 思路
用\(kmp\)找到字符串中长度相同的子串,前后都跑一边,那么长度为\(l\),到某个点\(x\),\(1 \to l = x \to x + l - 1\),点y,\(n - r \to n = y - r + 1 \to y\),然后就是判断\(y - r + 1 = x + l\)的点即可,然后就是找一点\(z\), \(1 \to l = z - l + 1 \to z\ and\ n - r \to n = z \to z + r - 1\),存在即答案加1,可以看出建图跑比较方便。
然后建两棵树的图即可,随意用一颗树的dfs序在另一颗树上跑主席树,可持久化记忆。然后每次查询即可
code :
int in[N][2], out[N][2], tot[2], pos[N]; int rt[N]; int ls[N << 8],rs[N << 8],sum[N << 8], idx; char str[N]; int f1[N],f2[N]; int kkk; int h1[N],e1[N],ne1[N],m1, h2[N], e2[N],ne2[N], m2; void ad1(int a,int b) { e1[m1] = b, ne1[m1] = h1[a], h1[a] = m1 ++; } void ad2(int a,int b) { e2[m2] = b, ne2[m2] = h2[a], h2[a] = m2 ++; } void dfs1(int u) { in[u][0] = ++ tot[0]; pos[tot[0]] = u; for(int i = h1[u];~i;i = ne1[i]) { int j = e1[i]; dfs1(j); } out[u][0] = tot[0]; } void dfs2(int u) { in[u][1] = ++ tot[1]; for(int i = h2[u];~i;i = ne2[i]) { int j = e2[i]; dfs2(j); } out[u][1] = tot[1]; } void insert(int &t,int pre,int l,int r,int x) { t = ++ idx; ls[t] = ls[pre]; rs[t] = rs[pre]; sum[t] = sum[pre] + 1; if(l == r) {return;} int mid = l + r >> 1; if(x <= mid) insert(ls[t],ls[pre],l,mid,x); else insert(rs[t],rs[pre],mid + 1,r,x); } int query(int p,int q,int l,int r,int x,int y) { if(r < x || y < l) return 0; if(x <= l && r <= y) return sum[p] - sum[q]; int mid = l + r >> 1; return query(ls[p],ls[q],l,mid,x,y) + query(rs[p],rs[q],mid + 1,r,x,y); } void solve(){ int n,q; cin >> n >> q; cin >> (str + 1); // 字符串kmp预处理 int j = 0; memset(h1,-1,sizeof h1); memset(h2,-1,sizeof h2); for(int i = 2;i <= n;i ++) { while(j && str[j + 1] != str[i]) j = f1[j]; if(str[j + 1] == str[i]) j ++; f1[i] = j; } j = n + 1; f2[n] = n + 1; for(int i = n - 1;i >= 1;i --) { while(j <= n && str[j - 1] != str[i]) j = f2[j]; if(str[j - 1] == str[i]) j --; f2[i] = j; } // 建树 for(int i = 1;i <= n;i ++) { ad1(f1[i],i); ad2(f2[i],i); } dfs1(0); dfs2(n + 1); // 处理 for(int i = 1;i <= n + 1;i ++) { // 注意我们的dfs序是从0或n + 1开始的,所以需要加1 insert(rt[i], rt[i - 1],1,n + 1,in[pos[i] + 1][1]); } while(q --) { int x,y; cin >> x >> y; y = n - y + 1; cout << query(rt[out[x][0]],rt[in[x][0] - 1],1,n + 1,in[y][1],out[y][1]) << endl; } for(int i = 0;i <= n + 1;i ++) f1[i] = f2[i] = 0, rt[i] = 0; idx = tot[1] = tot[0] = 0; m1 = m2 = 0; }
这篇关于2021“MINIEYE杯”中国大学生算法设计超级联赛(10)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南