zoukankan      html  css  js  c++  java
  • HZOI20190819模拟26题解

    题面:https://www.cnblogs.com/Juve/articles/11376806.html

    A. 嚎叫响彻在贪婪的厂房:

    是时候学习一下map和set的用法了。。。。。。

    贪心:区间[L,R]合法的条件:所有相邻两数差的绝对值的gcd不等于1,且没有重复的元素

    gcd比较好满足,判重用map或set都可以

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #include<set>
    #define MAXN 100005
    #define int long long
    using namespace std;
    int n,a[MAXN],d,ans=0;
    set<int>s;
    int gcd(int a,int b){
    	return b==0?a:gcd(b,a%b);
    }
    signed main(){
    	scanf("%lld",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%lld",&a[i]);
    	}
    	s.insert(a[1]);
    	for(int i=1;i<=n;i++){
    		d=gcd(d,abs(a[i]-a[i-1]));
    		if(d==1||s.find(a[i])!=s.end()){
    			ans++;
    			d=0;
    			s.clear();
    		}
    		s.insert(a[i]);
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    B. 主仆见证了 Hobo 的离别

    题解说什么离线建树。。。

    其实只用xgdfs在线搜索

    如果是交,我们就建立由新点指向原点的有向边,表示新点有的元素原点也有

    并就反过来,当然我们要特判k=1的情况,因为此时交和并等价,建双向边即可,

    查询的话dfs,只要x能到y就行

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #define MAXN 500005
    using namespace std;
    int n,m;
    int to[MAXN<<1],nxt[MAXN<<1],pre[MAXN],cnt=0;
    void add(int u,int v){
    	cnt++,to[cnt]=v,nxt[cnt]=pre[u],pre[u]=cnt;
    }
    bool dfs(int x,int fa,int opt){
    	if(x==opt) return 1;
    	for(int i=pre[x];i;i=nxt[i]){
    		int y=to[i];
    		if(y==fa) continue;
    		if(dfs(y,x,opt)) return 1;
    	}
    	return 0;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1,opt;i<=m;i++){
    		scanf("%d",&opt);
    		if(opt==0){
    			n++;
    			int op,k;
    			scanf("%d%d",&op,&k);
    			for(int j=1,x;j<=k;j++){
    				scanf("%d",&x);
    				if(k==1) add(x,n),add(n,x);
    				else if(op==0) add(n,x);
    				else if(op==1) add(x,n);
    			}
    		}else{
    			int x,y;
    			scanf("%d%d",&x,&y);
    			if(dfs(x,0,y)) puts("1");
    			else puts("0");
    		}
    	}
    	return 0;
    }
    

    C. 征途堆积出友情的永恒

    有一个明显的50分dp:

    设sum[i]表示前缀和,那么f[i]=min(f[j]+max(sum[i]-sum[j],b[j+1]))

    n2转移,再加上三个特判,可以得到80分:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define MAXN 500005
    #define int long long
    #define inf 0x7ffffffffffffff
    #define re register
    using namespace std;
    int n,k,a[MAXN],sum[MAXN],b[MAXN],f[MAXN];
    bool flag1=0,flag2=0;
    int max(int a,int b){
    	return a>b?a:b;
    }
    int min(int a,int b){
    	return a<b?a:b;
    }
    signed main(){
        scanf("%lld%lld",&n,&k);
    	for(re int i=1;i<=n;i++){
    		scanf("%lld",&a[i]);
    		sum[i]=sum[i-1]+a[i];
    	}
    	for(re int i=0;i<n;i++){
    		scanf("%lld",&b[i]);
    		if(b[i]!=1) flag1=1;
    		if(b[i]!=a[i+1]) flag2=1;
    		f[i+1]=inf;
    	}
    	if(flag1==0||flag2==0){
    		printf("%lld
    ",sum[n]);
    		return 0;
    	}
    	if(k==n){
    		printf("%lld
    ",max(b[0],sum[n]));
    		return 0;
    	}
    	for(re int i=1;i<=n;i++){
    		for(re int j=max(0,i-k);j<=i-1;j++)
    			f[i]=min(f[i],f[j]+max(sum[i]-sum[j],b[j]));
    	}
    	printf("%lld
    ",f[n]);
        return 0;
    }
    

    可以用堆优化dp:

    建两个小根堆,一个维护 f[j]+b[j+1] ,另一个维护 f[j]-sum[j] ,则答案为 min(第一个堆的最小值,第二个堆的最小值+sum[i]) 。
    如果取到的j小于i-k,则弹出堆顶继续取。
    在第一个堆中取数时,如果发现它比 f[j]-sum[j]+sum[i]小,则把j加进第二个堆,弹出第一个堆的堆顶继续取,直到取到符合条件的为止。
    显然,一个j至多进堆2次,出堆2次。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define MAXN 500005
    #define int long long
    #define inf 0x7ffffffffffffff
    #define re register
    using namespace std;
    int n,k,a[MAXN],sum[MAXN],b[MAXN],f[MAXN];
    int max(int a,int b){
    	return a>b?a:b;
    }
    int min(int a,int b){
    	return a<b?a:b;
    }
    struct node{
    	int val,pos;
    	friend bool operator < (node a,node b){
    		return a.val==b.val?a.pos>b.pos:a.val>b.val;
    	}
    };
    priority_queue<node>q1,q2;
    signed main(){
        scanf("%lld%lld",&n,&k);
    	for(re int i=1;i<=n;i++){
    		scanf("%lld",&a[i]);
    		sum[i]=sum[i-1]+a[i];
    	}
    	for(re int i=0;i<n;i++){
    		scanf("%lld",&b[i]);
    		f[i+1]=inf;
    	}
    	q1.push((node){f[0]+b[0],0});
    	for(re int i=1;i<=n;i++){
    		while(!q1.empty()&&(q1.top().pos<i-k)) q1.pop();
    		while(!q2.empty()&&(q2.top().pos<i-k)) q2.pop();
    		while(!q1.empty()){
    			int j=q1.top().pos;
    			if(q1.top().val>=f[j]-sum[j]+sum[i]) break;
    			q1.pop();
    			if(j<i-k) continue;
    			q2.push((node){f[j]-sum[j],j});
    		}
    		while(!q1.empty()&&(q1.top().pos<i-k)) q1.pop();
    		while(!q2.empty()&&(q2.top().pos<i-k)) q2.pop();
    		if(!q1.empty()) f[i]=min(f[i],q1.top().val);
    		if(!q2.empty()) f[i]=min(f[i],q2.top().val+sum[i]);
    		q1.push((node){f[i]+b[i],i});
    	}
    	printf("%lld
    ",f[n]);
        return 0;
    }
    
  • 相关阅读:
    第一次练习总结
    第一次上机总结
    写在程序组干活之前
    虚拟机Centos7安装Mysql
    第一章 开发体验
    如何优雅的移植JavaScript组件到Blazor
    Asp.net core中RedisMQ的简单应用
    docker容器安装mysql
    Centos 8安装Docker
    c# 定时启动一个操作、任务(版本2)
  • 原文地址:https://www.cnblogs.com/Juve/p/11377195.html
Copyright © 2011-2022 走看看