“科林明伦杯”哈尔滨理工大学第十届程序设计竞赛(同步赛)
差一点点就AK的一场。
差在高度相同的植物合并qwq
A. 点对最大值 (Nowcoder 5758 A)
题目大意
给定一棵树,有点权和边权,找到最大的点对价值。价值为两个点的点权和加上两点之间的边权和。
解题思路
考虑分治。
答案就是在根各子树中的最大值以及跨越根的最大值中取。
跨越根的最大值就是两个点,它们的点权加上到根节点的边权的和是第一大和第二大的。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N=1e6+8; int n,num; int head[N]; int nxt[N*2],to[N*2],cost[N*2]; int val[N]; LL ans=0; void add(int u,int v,int w){ num++; nxt[num]=head[u]; to[num]=v; cost[num]=w; head[u]=num; num++; nxt[num]=head[v]; to[num]=u; cost[num]=w; head[v]=num; } LL DFS(int u,int fa){ int cnt=0; LL qmq=val[u]; LL qnq=-9147483647; LL tmp; for(int v,i=head[u];i;i=nxt[i]){ v=to[i]; if (v==fa) continue; ++cnt; tmp=DFS(v,u)+cost[i]; ans=max(ans,tmp+val[u]); if (qmq<=tmp){ qnq=qmq; qmq=tmp; }else if (qnq<tmp){ qnq=tmp; } } ans=max(ans,qmq+qnq); return max(qmq,qnq); } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { read(n); num=0; for(int a,b,i=1;i<n;++i){ read(a); read(b); add(a,i+1,b); } for(int i=1;i<=n;++i){ read(val[i]); } DFS(1,1); write(ans,'\n'); ans=-9147483647; for(int i=0;i<=n;++i) head[i]=0; } return 0; }
B. 减成一 (Nowcoder 5758 B)
题目大意
给定一个数组,每次选择一个区间使区间的数减一,问进行多少次可以让所有数变成。
解题思路
NOIP都考了两次了......
答案就是差分数组的元素与取的和。
因为,假设相邻两个数,,如果,的在减少成的时候,就早就减成了,所以操作次数取大的那个。
而,那么在减到那一刻,早就变成了,而在变成之后,之后的那些数就不能减,只能等减完再搞后面的数,而后面的数减成1就还需要次,因为前次可以和之前减时一起操作。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int t; cin>>t; while(t--){ LL ans=0; int n; cin>>n; int la=1; int qwq=0; while(n--){ cin>>qwq; ans+=max(0,qwq-la); la=qwq; } cout<<ans<<endl; } return 0; }
C. 面积 (Nowcoder 5758 C)
题目大意
给定正方形边长,算一个图形面积。它由一个正方形和四个以其边长为直径的半圆构成。
解题思路
算就可以了。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; const double pi=3.14; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int t; cin>>t; while(t--){ LL x; cin>>x; double ans=x*x+2*pi*(x*1.0/2)*(x*1.0/2); cout<<fixed<<setprecision(2)<<ans<<endl; } return 0; }
D. 扔硬币 (Nowcoder 5758 D)
题目大意
同时扔枚硬币,正反概率相同。已知至少有枚硬币反面朝上,问恰有枚硬币正面朝上的概率是多少。
解题思路
条件概率。
当答案就是0.
否则就是
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int N = 1e5+8; const LL mo=1e9+7; LL jie[N+1],invjie[N+1]; LL qpower(LL a,LL b){ LL qwq=1; while(b){ if (b&1) qwq=qwq*a%mo; b>>=1; a=a*a%mo; } return qwq; } LL inv(LL qwq){ return qpower(qwq,mo-2); } LL C(int n,int m){ if (n<m) return 0; else return jie[n]*invjie[m]%mo*invjie[n-m]%mo; } LL sum(int n,int l,int r){ LL qwq=0; for(int i=l;i<=r;++i){ qwq=(qwq+C(n,i))%mo; } return qwq; } int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); jie[0]=jie[1]=invjie[0]=invjie[1]=1; for(int i=2;i<=N-3;++i){ jie[i]=jie[i-1]*i%mo; invjie[i]=inv(jie[i]); } int t; cin>>t; while(t--){ int n,m,k; cin>>n>>m>>k; LL ans=0; if (n-m>=k) ans=C(n,k)*inv(sum(n,m,n))%mo; cout<<ans<<endl; } return 0; }
E. 赛马 (Nowcoder 5758 E)
题目大意
田忌赛马。已知对方马的出场顺序和战斗力,安排自己的马的出场顺序得到最大赢数。
解题思路
对方马战力从小到大依次匹配我方大于其战斗力的最小战斗力的马。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { int n; read(n); multiset<int> qwq; for(int u,i=1;i<=n;++i){ read(u); qwq.insert(u); } vector<int> b(n); for(int i=1;i<=n;++i) read(b[i-1]); sort(b.begin(),b.end()); int ans=0; for(auto i:b){ auto it=qwq.upper_bound(i); if (it!=qwq.end()){ ans++; qwq.erase(it); } } write(ans,'\n'); } return 0; }
F. 三角形 (Nowcoder 5758 F)
题目大意
将长为的木棒分割成若干段长度为正整数的小木棒,要求任意三段都不能组成三角形,问最多分割成的小木棒的个数。
解题思路
斐波那契数列就是恰好不能组成三角形的边长极限。对其求和直到刚好大于等于即可。
如果分割有剩余的话那这个就不能分割的。
数很大最好用int128。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { __int128 n; read(n); __int128 a,b,c; a=1; b=1; n=n-2; int cnt=1; while(n>=0){ cnt++; c=a+b; n=n-c; a=b; b=c; } write(cnt,'\n'); } return 0; }
G. 养花 (Nowcoder 5758 G)
题目大意
小明有棵植物,所有植物排成一排,植物的初始高度为数组,他想让植物的高度都恰好达到,小明有瓶药水,但药水分为种:
- 选择一棵高度为的植物变为高度的植物
- 选择一棵高度在区间内的植物变为高度的植物
- 选择一棵高度为的植物变为区间内某一高度的植物
- 选择一棵高度在区间内的植物变为区间内某一高度的植物
由于第瓶药水最多可以使用次,小明想知道他最多让多少棵植物高度达到。
解题思路
决策类问题,很明显带有反悔的抉择,考虑网络流。
不同药水之间可以进行转移,即对于某个植物,我们用了某个药水后可以用另一个药水。于是这些药水可以构成一张图,对于一个植物,它可以从某点出发,根据边进行转移,而某些药水可以到达终点高度,于是我们只要能到达那些药水即可。
首先药水之间相互连边,能够抵达指定高度的药水再与终点连边,边流量都是无穷。
对于植物,连向能给它施加的药水,边权为无穷。
再将植物与起点连边,边权为,表示只能施加一次该植物。
我们发现还有药的使用次数,它是流进该药或流出该药的流量和,我们要对它进行限制,于是我们把药拆成两个点,流进点和流出点,两者的连边的流量即为药的使用次数,这样就限制了药的使用次数。
此时我们发现会,因为植物数太多了,但注意到这里我们只关心高度,而高度最多只有,那么我们可以把同高度的植物合并。
设高度为的植物出现了次,那么起点就连向代表植物高度为的点,边容量就是。
然后跑一遍网络流,最大流即是答案。
综上,药拆点,限制药的流量。药流出点与能抵达的药流进点连边,以及和可抵达的终点连边,起点和植物高度连边,植物高度再和相应的药的流进点连边。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N=4e6; const int M=2e5; const int INF=1e9+7; int head[M],nxt[N*2],to[N*2],team[M],dis[M]; LL flow[N*2]; int n,e,st,en,num,m,k; void add(int u, int v, int w) { num++; nxt[num] = head[u]; to[num] = v; flow[num] = w; head[u] = num; num++; nxt[num] = head[v]; to[num] = u; flow[num] = 0; head[v] = num; } bool BFS() { int l = 0, r = 1; team[1] = st; for(int i=0;i<=en;++i) dis[i]=0; dis[st] = 1; while (l < r) { int u = team[++l]; for (int v, i = head[u]; i; i = nxt[i]) { v = to[i]; if (dis[v] == 0 && flow[i]) { dis[v] = dis[u] + 1; team[++r] = v; } } } if (dis[en]) return true; else return false; } LL DFS(int u, LL f) { if (u == en) return f; LL qwq = 0, tmp = 0; for (int v, i = head[u]; i; i = nxt[i]) { v = to[i]; if (dis[v] == dis[u] + 1 && flow[i]) { qwq = DFS(v, min(f - tmp, flow[i])); flow[i] -= qwq; flow[i ^ 1] += qwq; tmp += qwq; if (tmp == f) return tmp; } } return tmp; } struct yao{ int z; int cnt; int a1,a2,b1,b2; }yy[1006]; int cnt[105]; int main(void) { int kase; read(kase); num=1; for (int ii = 1; ii <= kase; ii++) { read(n); read(m); read(k); for(int h,i=1;i<=n;++i){ read(h); cnt[h]++; } for(int i=1;i<=m;++i){ read(yy[i].z); read(yy[i].cnt); if (yy[i].z==1){ read(yy[i].a1); read(yy[i].b1); }else if (yy[i].z==2){ read(yy[i].a1); read(yy[i].a2); read(yy[i].b1); }else if (yy[i].z==3){ read(yy[i].a1); read(yy[i].b1); read(yy[i].b2); }else{ read(yy[i].a1); read(yy[i].a2); read(yy[i].b1); read(yy[i].b2); } } st=0; en=2*m+1+100; for(int i=1;i<=100;++i){ add(st,i+2*m,cnt[i]); } for(int i=1;i<=m;++i){ add(2*i-1,2*i,yy[i].cnt); } for(int i=1;i<=m;++i){ if (yy[i].z==1||yy[i].z==2){ if (yy[i].b1==k) add(i*2,en,INF); }else{ if (yy[i].b1<=k&&yy[i].b2>=k) add(i*2,en,INF); } } for(int i=1;i<=100;++i){ for(int j=1;j<=m;++j){ if (yy[j].z==1||yy[j].z==3){ if (yy[j].a1==i) add(2*m+i,2*j-1,INF); }else{ if (yy[j].a1<=i&&yy[j].a2>=i) add(2*m+i,2*j-1,INF); } } } for(int i=1;i<=m;++i){ for(int j=1;j<=m;++j){ if (i==j) continue; if (yy[i].z==1||yy[i].z==2){ if (yy[j].z==1||yy[j].z==3){ if (yy[i].b1==yy[j].a1) add(2*i,2*j-1,INF); }else{ if (yy[i].b1>=yy[j].a1&&yy[i].b1<=yy[j].a2) add(2*i,2*j-1,INF); } }else{ if (yy[j].z==1||yy[j].z==3){ if (yy[i].b1<=yy[j].a1&&yy[i].b2>=yy[j].a1) add(2*i,2*j-1,INF); }else{ if (!(yy[i].b1>yy[j].a2||yy[i].b2<yy[j].a1)) add(2*i,2*j-1,INF); } } } } int ans=0; while(BFS()){ ans+=DFS(st,INF); } write(ans,'\n'); for(int i=0;i<=en;++i) head[i]=0; for(int i=1;i<=100;++i) cnt[i]=0; num=1; } return 0; }
H. 直线 (Nowcoder 5758 H)
题目大意
平面上的条直线最多有多少个交点。
解题思路
学过高中数学的都知道答案是
用即可。 或者python
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { __int128 n; read(n); __int128 ans = n*(n-1)/2; write(ans,'\n'); } return 0; }
I. 字典序 (Nowcoder 5758 I)
题目大意
给定一个个数的数组,记表示删去第个数(从开始)后的数组,现对这个数组按照字典序从小到大排序,问最后这些数组的是多少。
解题思路
对这关于的全排列快排即可,我们考虑比较函数。
设两个位置,我们考虑如何得知分别删去后的两个数组的字典序大小。
设,
那么对于位置的数,两个数组是相等的。
对于位置的数,两个数组也是相等的。
我们要比较的就是和谁大谁小。
很显然我们可以依次比较,但这会超时。
注意到这里其实比较的都是当前位置与后一个位置的数的大小关系。
而我们要找的就是从第位起第一个不是相等的位置,从这个位置就可以判断两者的大小。
预处理即可。
表示第位和第位的大小关系,相等为,大于为,小于为。
表示从第位起第一个不为的位置。
如果的返回值与其相反。
(其实做法和之前北大美团杯的交互题非常类似)
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N=1e5+8; int n; int a[N]; int ans[N]; int sign[N]; int nxt[N]; bool cmp(int x,int y){ int mi=x<y?x:y; int ma=x+y-mi; int cur=nxt[mi]; int si=x>y; if (cur>=ma) return (1^si); else if (sign[cur]==-1) return (0^si); else return (1^si); } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { read(n); for(int i=1;i<=n;++i){ read(a[i]); ans[i]=i; } for(int i=1;i<n;++i){ if (a[i]>a[i+1]) sign[i]=1; else if (a[i]==a[i+1]) sign[i]=0; else sign[i]=-1; } int cur=n+8; for(int i=n-1;i>0;--i){ if (sign[i]!=0) cur=i; nxt[i]=cur; } sort(ans+1,ans+1+n,cmp); for(int i=1;i<=n;++i){ printf("%d%c",ans[i],i==n?'\n':' '); } } return 0; }
J. 最大值 (Nowcoder 5758 J)
题目大意
给定一个字符串,求该字符串长度最大的非前缀子串,使得它恰好为的前缀。
解题思路
注意到结果对于长度具有单调性,二分长度,拿长度为的前缀进行匹配即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N = 1e5+8; char s[N],ss[N]; int nxt[N],len; char tmp[N]; void mk(){ nxt[0]=0; int k=0; for(int i=1;i<len-1;++i){ while(k>0&&ss[i]!=ss[k]) k=nxt[k-1]; if (ss[k]==ss[i]) ++k; nxt[i]=k; } } bool check(int l){ for(int i=0;i<l;++i){ tmp[i]=s[i]; } int q=0; for(int i=0;i<len-1;++i){ while(q>0&&ss[i]!=tmp[q]) q=nxt[q-1]; if (tmp[q]==ss[i]) ++q; if (q==l) return true; } return false; } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { scanf("%s",s); int l=0; int r=strlen(s); len=r; for(int j=0;j<len-1;++j) ss[j]=s[j+1]; mk(); while(l+1<r){ int mid=(l+r)>>1; if (check(mid)) l=mid; else r=mid; } write(l,'\n'); } return 0; }
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/13021499.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步