HZOJ 20190819 NOIP模拟26题解
考试过程:
照例开题,然后觉得三道题都挺难,比昨天难多了(flag×1),T1 dp?T2 数据结构? T3 dp?事实证明我是sb然后决定先搞T2,但是,woc,这题在说什么啊,我怎么看不懂题啊,连样例都手模不出来,完了凉了,然后又看了一个半小时的题,还是没看懂心态爆炸,然后匆匆打了T1T3暴力,还自我感觉良好,觉得这么难的题,拿个150pts左右应该rank10没啥问题叭,然后出分,T1T2A了一片,然后我还暴力挂分,然后在第二机房成功倒数。
反思:
还是思考的太少啊,对题目的难度评估出现很大偏差,考试时还有时走思,这布星啊,这状态真tm差
题解:
T1 嚎叫响彻在贪婪的厂房:
其实很简单,用STL可以过掉(map或set都行,主要作用还是判重),比较显然的贪心,就是能不断就不断,感性理解一下即可,证明也挺简单的叭我就不证了我才不会说是我不会证呢考场上想到这个了,但是还是sb的打了dp,真zz啊,改了STL就能过啊。STL这些东西还是要学的啊很好用的啊
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1e5+10; 4 map<int,int> mp; 5 int a[N]; 6 int gcd(int a,int b){return b?gcd(b,a%b):a;} 7 int main(){ 8 int n; 9 scanf("%d",&n); 10 for(int i=1;i<=n;++i) scanf("%d",&a[i]); 11 int ans=0;int l=1; 12 while(l<=n){ 13 int r=l+1; 14 int k=abs(a[r]-a[l]); 15 mp[a[l]]=1; 16 while((r<=n)&&(gcd(abs(a[r]-a[l]),k)!=1)&&(mp.count(a[r])==0)){ 17 mp[a[r]]=1; 18 r++; 19 k=gcd(abs(a[r]-a[l]),k); 20 } 21 r--;l=r+1;ans++;mp.clear(); 22 } 23 printf("%d",ans); 24 }
T2 主仆见证了 Hobo 的离别:
考试时根本没看懂题,浪费一个半小时,然后就成功爆零滚粗,思博出题人语文比我还差啊艹
实际上这题只要,建边跑个dfs序,然后判一下k==1建双向边即可。实际上因为数据水直接在线dfs理论复杂度$O(n^2)$就可以水过于是旁边zkt大佬就hack别人AC代码hack了一下午233
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=50000005; 4 int first[N],nex[N<<1],to[N<<1],tot,fr[N]; 5 int fa[N]; 6 int judge; 7 void add(int a,int b){ 8 to[++tot]=b,nex[tot]=first[a],first[a]=tot;fr[tot]=a; 9 } 10 void dfs(int x,int fa,const int tar){//cout<<x<<" "<<endl; 11 if(x==tar) {judge=1;return ;} 12 for(int i=first[x];i;i=nex[i]){ 13 int y=to[i]; 14 if(y==fa) continue; 15 16 dfs(y,x,tar); 17 } 18 } 19 int main(){ 20 int n,m; 21 scanf("%d%d",&n,&m); 22 int qsum=0; 23 for(int i=1;i<=m;++i){ 24 int opt; 25 scanf("%d",&opt); 26 if(opt==0){ 27 int op,kk;n++; 28 scanf("%d%d",&op,&kk); 29 for(int j=1;j<=kk;++j){//cout<<id<<endl; 30 int x; 31 scanf("%d",&x); 32 if(kk==1){ 33 fa[n]=x,fa[x]=n; 34 add(n,x);add(x,n); 35 break; 36 } 37 if(op) {add(x,n);fa[x]=n;} 38 else {add(n,x);fa[n]=x;} 39 } 40 } 41 else{ 42 judge=0; 43 int a,b; 44 scanf("%d%d",&a,&b); 45 dfs(a,0,b);//cout<<"M"<<endl; 46 printf("%d\n",judge); 47 } 48 } 49 }
T3 征途堆积出友情的永恒:
其实是比较有难度的题,但是50pts暴力和30pts骗分很好打,我明明打对了骗分还是没分啊艹
正解是堆优化dp,首先考虑暴力转移式子$f[i]=min(f[j]+max(sum[i]-sum[j],b[j]))$
我们注意到对于固定的$j$,$f[j]-sum[j]$和$f[j]+b[j]$是固定不变的,所以考虑从这两个量入手,因为我们每次只要最小的,所以用两个堆分别维护一下就好了,具体实现方法是,首先要把两个堆中不满足距离小于k的点pop掉,然后把第一个堆中小于第二个堆顶$+sum[i]$ 的点pop掉并加入第二个堆中,直到符合条件为止,再把不满足距离小于k的点pop掉,最后在更新f数组即可
感觉优化dp的题做起来还是很无力啊
看博客看到的whs dalao的dp优化总结还是挺好的,就放过来了
总结:
1.对于这种dp优化,若dp式子中出现变量很少而定量很多,就要考虑到维护定量;
2.而对于dp式子中有max(),min()之类的,说明主动决策决定被动决策;所以考虑维护两个决策中较容易维护的一方;然后让另一方成为被动;若遇到维护的值不再偏向于己方;
那就把这种状态pop掉,转换成另一方;让这种状态继续合法;对答案做贡献;
3.注意2中max的决策单调性;例如这个题中max有单调性,就可以无后效性的转化;
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 const int N=500005; 5 const int INF=1e9+9; 6 int a[N],b[N],sum[N],dp[N],n,k; 7 int f[N]; 8 struct node{ 9 int id,val; 10 bool operator < (const node a) const{ 11 return val>a.val; 12 } 13 }; 14 priority_queue<node>q1,q2; 15 int min(int a,int b){return a<b?a:b;} 16 int max(int a,int b){return a>b?a:b;} 17 void work_2(){ 18 memset(f,0x3f,sizeof(f));f[0]=0; 19 //q1.push(dp[1]);q2.push(dp[1]+b[1]); 20 //q1.push((node){0,0}),q2.push((node){0,0}); 21 //q1.push((node){0,INF}),q2.push((node){0,INF}); 22 for(int i=1;i<=n;++i){ 23 q1.push((node){i-1,f[i-1]+b[i-1]}); 24 //q2.push((node){i-1,f[i-1]-sum[i-1]}); 25 while(q1.size()){ 26 int x=q1.top().id; 27 if(x>=i-k) break; 28 q1.pop(); 29 }//cout<<"L"<<endl; 30 while(q2.size()){ 31 int h=q2.top().id; 32 if(h>=i-k) break; 33 q2.pop(); 34 } 35 while(q1.size()){ 36 int y=q1.top().val; 37 int x=q1.top().id; 38 if(y>f[x]-sum[x]+sum[i]) break; 39 q1.pop(); 40 q2.push((node){x,f[x]-sum[x]}); 41 }//cout<<"M"<<endl; 42 while(q1.size()){ 43 int x=q1.top().id; 44 if(x>=i-k) break; 45 q1.pop(); 46 }//cout<<"K"<<endl; 47 while(q2.size()){ 48 int h=q2.top().id; 49 if(h>=i-k) break; 50 q2.pop(); 51 } 52 if(q1.size()) f[i]=min(f[i],q1.top().val); 53 if(q2.size()) f[i]=min(q2.top().val+sum[i],f[i]); 54 //f[i]=min(q1.top().val,q2.top().val+sum[i]); 55 //cout<<f[i]<<endl; 56 } 57 cout<<f[n]<<endl; 58 } 59 signed main(){ 60 scanf("%lld%lld",&n,&k); 61 for(int i=1;i<=n;++i) {scanf("%lld",&a[i]);sum[i]=sum[i-1]+a[i];} 62 for(int i=0;i<n;++i) {scanf("%lld",&b[i]);} 63 work_2(); 64 }