• 【超好懂的比赛题解】HNCPC Multi-university Training Round2 比赛题解(AHBGIK)



    title : HNCPC Multi-university Training Round2
    date : 2022-7-29
    tags :ACM,练习记录
    author : LINNO


    HNCPC Multi-university Training Round2比赛题解(AHBGIK)

    A - Harvest

    签到题,给定n个区间和k个点,问有多少个区间内有点。

    排序然后二分查找所在区间就行了。

    //#pragma GCC optimize("Ofast", "inline", "-ffast-math")
    //#pragma GCC target("avx,sse2,sse3,sse4,mmx")
    #include
    using namespace std;
    const int N=2e5+7;
    const int mod=1e9+7;
    
    //int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
    //void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
    
    int n,k,l[N],r[N],h[N],ans=0;
    
    void Solve(){
    	cin>>n;
    	for(int i=1;i<=n;++i){
    		cin>>l[i]>>r[i];
    	}
    	cin>>k;
    	for(int i=1;i<=k;++i) cin>>h[i];
    	sort(h+1,h+1+k);
    	for(int i=1;i<=n;++i){
    		int p=lower_bound(h+1,h+1+k,l[i])-h;
    		if(p!=k+1&&r[i]>=h[p]) ans++;
    	}
    	cout<<ans<<"\n";
    }
    
    signed main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0);cout.tie(0);
    //  freopen("in.cpp","r",stdin);
    //  freopen("out.cpp","w",stdout);
    	int T=1;
    //	cin>>T;
    //	clock_t start,finish;
    //	start=clock();
    	while(T--){
    		Solve();
    	}
    //	finish=clock();
    //	cerr<<((double)finish-start)/CLOCKS_PER_SEC<
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    H - include

    比较板的图论缩点。要求输出所有强连通分量中编号最小的点,直接在tarjan过程中记录最小那个数即可。

    #include
    using namespace std;
    const int N=30007,M=5e5+7;
    
    struct E{
    	int v,nxt;
    }e[M];
    
    int head[N],cnt=0;
    inline int addedge(int u,int v){
    	e[++cnt]=(E){v,head[u]};head[u]=cnt;
    }
    int n,m;
    int low[N],dfn[N],idx=0,stk[N],top=0,vis[N],bel[N],deg[N],mi[N];
    vector<int>vt;
    
    inline void tarjan(int x){
    	low[x]=dfn[x]=++idx;
    	stk[++top]=x;
    	vis[x]=1;
    	for(int i=head[x];i;i=e[i].nxt){
    		int to=e[i].v;
    		if(!dfn[to]){
    			tarjan(to);
    			low[x]=min(low[x],low[to]);
    		}else if(vis[to]) low[x]=min(low[x],dfn[to]);
    	}
    	if(dfn[x]==low[x]){
    		int y;
    		while(y=stk[top--]){
    			mi[x]=min(mi[x],y);  //记录集合中的最小数 
    			vis[y]=0;
    			bel[y]=x;
    			if(x==y) break;
    		}
    	}
    }
    
    signed main(){
    	cin>>n>>m;
    	for(int i=1;i<=n;++i) bel[i]=mi[i]=i;
    	for(int i=1,u,v;i<=m;++i){
    		cin>>u>>v;
    		addedge(u,v);
    	}
    	for(int i=1;i<=n;++i){
    		if(!dfn[i]) tarjan(i);
    	}
    	for(int i=1;i<=n;++i){
    		for(int j=head[i];j;j=e[j].nxt){
    			int to=e[j].v;
    			if(bel[to]!=bel[i]) ++deg[bel[to]];
    		}
    	}
    	//for(int i=1;i<=n;++i) cout<
    	for(int i=1;i<=n;++i){
    		if(bel[i]==i&&!deg[i]){
    			vt.emplace_back(mi[i]);
    		}
    	}
    	sort(vt.begin(),vt.end());
    	for(int i=0;i<vt.size();++i) cout<<vt[i]<<"\n";
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    B - Parse the Syntax Tree

    队友做的,题目大致意思就是给你n*m的字符来表示一棵数作为表达式,然后让你计算结果。直接模拟即可,找下一行相邻两个运算符或数字连边也好做。

    #include 
    #include 
    #include 
    #include 
    #include 
    #define int long long
    using namespace std;
    struct tree {
    	int lc,rc,ans;
    	char v;
    } a[100005];
    int n,m,tot,b[100][100];
    char c,ch[100][100];
    signed dfs(int x) {
    	if (a[x].lc==0&&a[x].rc==0) {
    		a[x].ans=a[x].v-'0';
    		return 0;
    	}
    	dfs(a[x].lc);
    	dfs(a[x].rc);
    	if (a[x].v=='+') a[x].ans=a[a[x].lc].ans+a[a[x].rc].ans;
    	if (a[x].v=='-') a[x].ans=a[a[x].lc].ans-a[a[x].rc].ans;
    	if (a[x].v=='*') a[x].ans=a[a[x].lc].ans*a[a[x].rc].ans;
    	return 0;
    }
    signed main() {
    	cin>>n>>m;
    	if (n==1) {
    		for (int i=1; i<=m; ++i) {
    			cin>>c;
    			if (c!='.') {
    				cout<<c;
    				return 0;
    			}
    		}
    	}
    	for (int i=1; i<=n; ++i) {
    		int num=0;
    		for (int j=1; j<=m; ++j) {
    			cin>>ch[i][j];
    			if (ch[i][j]!='.') {
    				++num;
    				++tot;
    				a[tot].v=ch[i][j];
    				b[i][j]=tot;
    				if (num%2==1) {
    					for (int k=j; k<=m; ++k) if (ch[i-1][k]!='.') {
    							a[b[i-1][k]].lc=tot;
    							break;
    						}
    				} else {
    					for (int k=j; k>=1; k--) if (ch[i-1][k]!='.') {
    							a[b[i-1][k]].rc=tot;
    							break;
    						}
    				}
    			}
    		}
    	}
    	dfs(1);
    	cout<<a[1].ans;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    G - Pizza delivery

    你要送n个外卖,每次送完需要回到店里取货。给定每个顾客的外卖从店出发需要的用时t以及不满意系数a,每个顾客对总不满意度的贡献为 s i = a i ∗ ( h i + p i ) s_i=a_i*(h_i+p_i) si=ai(hi+pi),其中p为排在他前面送的顾客数,h为从0时刻起到送达的总时间,求一个送货方案使得总不满意度最少。

    一眼看就是排序不等式,不过式子是队友推的。

    #include 
    #include 
    #include 
    #include 
    #include 
    #define int long long 
    using namespace std;
    struct dat
    {
    	int t,a;
    	double c;
    }a[100005];
    int n,ans,tt;
    bool cmp(dat a,dat b)
    {
    	return (2*a.t+b.t)*b.a-a.a+a.a*a.t<(a.t+2*b.t)*a.a-b.a+b.a*b.t;
    }
    signed main()
    {
    	cin>>n;
    	for (int i=1;i<=n;++i)
    	 {
    	 	scanf("%d%d",&a[i].t,&a[i].a);
    	 }
    	sort(a+1,a+1+n,cmp);
    	for (int i=1;i<=n;++i)
    	 {
    	 	ans+=a[i].a*(tt+a[i].t+i-1);
    	 	tt+=a[i].t*2;
    	 }
    	cout<<ans;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    I - (N+1)-legged race

    给定S个每个学生的力量a和身高h,规定团队的总战斗力为 ∑ i = 1 N A r i − ∑ i = 1 n − 1 ∣ H r i − H r i − 1 ∣ \sum_{i=1}^NA_{ri}-\sum_{i=1}^{n-1}|H_{ri}-H_{ri-1}| i=1NArii=1n1HriHri1

    要求从S个挑出N个学生使得战斗力最大。

    显然对挑出来的n个学生可以从高到低排一下序,那么总战斗力为 ∑ A i − h m a x + h m i n \sum A_i-h_{max}+h_{min} Aihmax+hmin,因此对于确定最高和最矮的k个人来说,只用挑中间A最大的即可。那么在枚举过程中,我们确认了最矮的那个(起点),就可以去递推选k个人时的最大战斗力。 d p [ i ] [ j ] 表示前 i 个人选 j 能得到的最大战斗力 , 转移式见代码 dp[i][j]表示前i个人选j能得到的最大战斗力,转移式见代码 dp[i][j]表示前i个人选j能得到的最大战斗力,转移式见代码

    //#pragma GCC optimize("Ofast", "inline", "-ffast-math")
    //#pragma GCC target("avx,sse2,sse3,sse4,mmx")
    #include
    #define inf 0x3f3f3f3f
    #define int long long
    using namespace std;
    const int N=1e5+7;
    const int mod=1e9+7;
    
    //int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
    //void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
    
    struct node{
    	int a,h;
    }s[N];
    
    int S,n,dp[N][205],ans=-inf;
    
    bool cmp(node A,node B){
    	return A.h<B.h;
    }
    
    void Solve(){
    	cin>>S>>n;
    	for(int i=1;i<=S;++i){
    		cin>>s[i].a>>s[i].h;
    	}
    	sort(s+1,s+1+S,cmp);
    	for(int j=1;j<=S;++j){
    		dp[j][1]=max(dp[j-1][1],s[j].a+s[j].h); //前j-1个选1个或者只选当前位置 
    		for(int k=2;k<n;++k){
    			dp[j][k]=max(dp[j-1][k],dp[j-1][k-1]+s[j].a);
    		}
    		if(j>=n) ans=max(ans,dp[j-1][n-1]+s[j].a-s[j].h);
    	}
    	cout<<ans<<"\n";
    }
    
    signed main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0);cout.tie(0);
    //  freopen("in.cpp","r",stdin);
    //  freopen("out.cpp","w",stdout);
    	int T=1;
    //	cin>>T;
    //	clock_t start,finish;
    //	start=clock();
    	while(T--){
    		Solve();
    	}
    //	finish=clock();
    //	cerr<<((double)finish-start)/CLOCKS_PER_SEC<
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    K - Zombie Land 2

    给定每个点的初始位置和在x轴,y轴方向的速度分量,问每个人被感染的最短时间。

    这道题一样看去就最短路或者最小生成树,但是有点细节,因为我卡精度卡了一个晚上。(应该就是卡精)。我们把两个点能够相互感染的时间求出来,大概是一个一元二次方程,判一下有没有解,有的话可以连一条边,然后跑一下Prim就出来了。

    //#pragma GCC optimize("Ofast", "inline", "-ffast-math")
    //#pragma GCC target("avx,sse2,sse3,sse4,mmx")
    #include
    #define int long long
    using namespace std;
    const int N=2007;
    
    //int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
    //void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
    
    struct P{
    	double x,y,vx,vy;
    	P(){};
    	P(double x,double y,double vx,double vy) : x(x),y(y),vx(vx),vy(vy){}
    }p[N];
    
    P operator +(P B,double A){
    	return P(B.x+B.vx*A,B.y+B.vy*A,B.vx,B.vy);
    }
    
    struct edge{
    	double w;
    	int u,v;
    };
    
    int n,d,vis[N],num=0;
    double sht[N];
    #define pii pair<double,int>
    #define mk make_pair
    
    double check_met(P A,P B){ //获取两点距离小于等于d的最小时间
    	double dx,dy,dvx,dvy,a,b,c,del,x1,x2;
    	dx=A.x-B.x;dvx=A.vx-B.vx;
    	dy=A.y-B.y;dvy=A.vy-B.vy;
    	a=dvx*dvx+dvy*dvy;
    	b=2ll*(dx*dvx+dy*dvy);
    	c=dx*dx+dy*dy-d*d; 
    	if(a==0){
    		if(c<=0) return 0;
    		else return -1;
    	}
    	del=b*b-4ll*a*c;
    	if(del<0) return -1; //方程无解 
    	x1=(-b-sqrt(del))/(2ll*a),x2=(-b+sqrt(del))/(2ll*a);
    	if(x1>x2) swap(x1,x2);
    	if(x2<0) return -1;
    	if(x1>0) return x1;
    	return 0;  //两点相互感染的时间 
    }
    
    inline void prim(){
    	vis[0]=1;
    	queue<edge>q;
    	for(int i=1;i<=n;++i) sht[i]=1e12;
    	sht[0]=0;
    	for(int i=1;i<=n;++i){
    		double tt=check_met(p[0],p[i]);
    		if(tt>=0) q.push((edge){tt,0,i});
    	}
    	while(q.size()){
    		edge e=q.front();
    		q.pop();
    		if(sht[e.v]<=e.w) continue;
    		sht[e.v]=e.w;
    		vis[e.v]=1;
    		for(int i=1;i<=n;++i){
    			double tt=check_met(p[e.v]+e.w,p[i]+e.w);
    			if(tt>=0) q.push((edge){tt+sht[e.v],e.v,i});
    		}
    	}
    }
    
    void Solve(){
    	cin>>n>>d;
    	//scanf("%d%lld",&n,&d);
    	for(int i=0;i<=n;++i){
    		//scanf("%Lf%Lf%Lf%Lf",&p[i].x,&p[i].y,&p[i].vx,&p[i].vy);
    		cin>>p[i].x>>p[i].y>>p[i].vx>>p[i].vy;
    	}
    	prim();
    	for(int i=1;i<=n;++i){
    		if(!vis[i]) printf("-1\n");//cout<<-1<<"\n";
    		else printf("%.10lf\n",sht[i]);//cout<
    	}
    }
    
    signed main(){
    //	ios::sync_with_stdio(0);
    //	cin.tie(0);cout.tie(0);0
    //  freopen("in.cpp","r",stdin);
    //  freopen("out.cpp","w",stdout);
    	int T=1;
    //	cin>>T;
    //	clock_t start,finish;
    //	start=clock();
    	while(T--){
    		Solve();
    	}
    //	finish=clock();
    //	cerr<<((double)finish-start)/CLOCKS_PER_SEC<
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
  • 相关阅读:
    Java集合源码剖析——基于JDK1.8中ConcurrentHashMap的实现原理
    Matlab图像处理-从RGB转换为HSV
    GIS技巧之一键下载城市路网数据
    潘多拉 IOT 开发板学习(HAL 库)—— 实验8 定时器中断实验(学习笔记)
    深入理解Scikit-Learn中的分层抽样:实现与应用
    pandas使用dataframe中的两列时间对象数据列作差生成时间差数据列、指定时间数据列减去timedelta数据列实现数据偏移(向后偏移、时间减小)
    HBase表的RowKey设计、热点和二级索引
    Vmware安装Kali
    Kafka
    python标准库操作
  • 原文地址:https://blog.csdn.net/SC_Linno/article/details/126050884