“科林明伦杯”哈尔滨理工大学第十届程序设计竞赛(同步赛)

差一点点就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)

题目大意

给定一个数组,每次选择一个区间使区间的数减一,问进行多少次可以让所有数变成1

解题思路

NOIP都考了两次了......

答案就是差分数组的元素与0max的和。

因为,假设相邻两个数a,b,如果a<bb的在减少成1的时候,a就早就减成1了,所以操作次数取大的那个。

a>b,那么a在减到1那一刻,b早就变成1了,而在b变成1之后,b之后的那些数就不能减,只能等a减完再搞b后面的数,而b后面的数c减成1就还需要cb次,因为前b次可以和之前减a时一起操作。

神奇的代码
#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)

题目大意

同时扔n枚硬币,正反概率相同。已知至少有m枚硬币反面朝上,问恰有k枚硬币正面朝上的概率是多少。

解题思路

条件概率。

nm<k答案就是0.
否则就是Cnki=0nmCnm+i

神奇的代码
#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)

题目大意

将长为a的木棒分割成若干段长度为正整数的小木棒,要求任意三段都不能组成三角形,问最多分割成的小木棒的个数。

解题思路

斐波那契数列就是恰好不能组成三角形的边长极限。对其求和直到刚好大于等于a即可。

如果分割有剩余的话那这个就不能分割的。

数很大最好用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)

题目大意

小明有n棵植物,所有植物排成一排,植物的初始高度为数组h,他想让植物的高度都恰好达到k,小明有m瓶药水,但药水分为4种:

  • 选择一棵高度为a0的植物变为b0高度的植物
  • 选择一棵高度在[a1,a2]区间内的植物变为b1高度的植物
  • 选择一棵高度为a1的植物变为[b1,b2]区间内某一高度的植物
  • 选择一棵高度在[a1,a2]区间内的植物变为[b1,b2]区间内某一高度的植物

由于第i瓶药水最多可以使用Ci次,小明想知道他最多让多少棵植物高度达到k

解题思路

决策类问题,很明显带有反悔的抉择,考虑网络流。

不同药水之间可以进行转移,即对于某个植物,我们用了某个药水后可以用另一个药水。于是这些药水可以构成一张图,对于一个植物,它可以从某点出发,根据边进行转移,而某些药水可以到达终点高度,于是我们只要能到达那些药水即可。

首先药水之间相互连边,能够抵达指定高度的药水再与终点连边,边流量都是无穷。

对于植物,连向能给它施加的药水,边权为无穷。

再将植物与起点连边,边权为1,表示只能施加一次该植物。

我们发现还有药的使用次数,它是流进该药或流出该药的流量和,我们要对它进行限制,于是我们把药拆成两个点,流进点和流出点,两者的连边的流量即为药的使用次数,这样就限制了药的使用次数。

此时我们发现会T,因为植物数太多了,但注意到这里我们只关心高度,而高度最多只有100,那么我们可以把同高度的植物合并。

设高度为i的植物出现了cnti次,那么起点就连向代表植物高度为i的点,边容量就是cnti

然后跑一遍网络流,最大流即是答案。

综上,药拆点,限制药的流量。药流出点与能抵达的药流进点连边,以及和可抵达的终点连边,起点和植物高度连边,植物高度再和相应的药的流进点连边。

神奇的代码
#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)

题目大意

平面上的n条直线最多有多少个交点。

解题思路

学过高中数学的都知道答案是n(n1)2

int128即可。 或者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)

题目大意

给定一个n个数的数组A,记Si表示删去第i个数(从1开始)后的数组,现对这n个数组按照字典序从小到大排序,问最后这些数组的是多少。

解题思路

对这关于1n的全排列快排即可,我们考虑比较函数。

设两个位置i,j,我们考虑如何得知分别删去i,j后的两个数组的字典序大小。

i<j

那么对于cur<i位置的数,两个数组是相等的。

对于cur>j位置的数,两个数组也是相等的。

我们要比较的就是A[i+1...j]A[i...j1]谁大谁小。

很显然我们可以依次比较,但这会超时。

注意到这里其实比较的都是当前位置与后一个位置的数的大小关系。

而我们要找的就是从第i位起第一个不是相等的位置,从这个位置就可以判断两者的大小。

预处理即可。

sign[i]表示第i位和第i+1位的大小关系,相等为0,大于为1,小于为1

nxt[i]表示从第i位起第一个sign[i]不为0的位置。

如果i>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;
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)

题目大意

给定一个字符串a,求该字符串长度最大的非前缀子串,使得它恰好为a的前缀。

解题思路

注意到结果对于长度具有单调性,二分长度l,拿长度为l的前缀进行KMP匹配即可。

神奇的代码
#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 中国大陆许可协议进行许可。

posted @   ~Lanly~  阅读(294)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.