HZOI20190819模拟26题解
题面:https://www.cnblogs.com/Juve/articles/11376806.html
A. 嚎叫响彻在贪婪的厂房:
是时候学习一下map和set的用法了。。。。。。
贪心:区间[L,R]合法的条件:所有相邻两数差的绝对值的gcd不等于1,且没有重复的元素
gcd比较好满足,判重用map或set都可以
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 | #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\n" ,ans); return 0; } |
B. 主仆见证了 Hobo 的离别
题解说什么离线建树。。。
其实只用xgdfs在线搜索
如果是交,我们就建立由新点指向原点的有向边,表示新点有的元素原点也有
并就反过来,当然我们要特判k=1的情况,因为此时交和并等价,建双向边即可,
查询的话dfs,只要x能到y就行
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 | #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分:
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 | #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\n" ,sum[n]); return 0; } if (k==n){ printf ( "%lld\n" ,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\n" ,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次。
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 | #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\n" ,f[n]); return 0; } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· .NET 9 new features-C#13新的锁类型和语义
· Linux系统下SQL Server数据库镜像配置全流程详解
· Sdcb Chats 技术博客:数据库 ID 选型的曲折之路 - 从 Guid 到自增 ID,再到
· Winform-耗时操作导致界面渲染滞后
· Phi小模型开发教程:C#使用本地模型Phi视觉模型分析图像,实现图片分类、搜索等功能
· 语音处理 开源项目 EchoSharp
· drools 规则引擎和 solon-flow 哪个好?solon-flow 简明教程