纪中游记(二)
2019.08.01
与昨天简直就是天壤之别,心态大崩
写在前面的一句话
今天的题太难受了,大佬快来救我
T0(佳肴)
初
一看数据范围,二进制直接莽!
复
居然A了,(心中狂喜),原来这一题只需要枚举每个状态,再记录最小值就行。
终
大水题不解释。
T1(取数游戏)
初
乍看像博弈论
复
靠答案水了30分.
(听MHY大佬讲完以后觉得异常简单,于是粘一波OE大佬的题解)
~
第一眼以为是博弈论(我好像就没看对过)其实这题区间DP就能搞,但是我不会所以找题解,dalao的神奇转移方程让我懵逼幸好周围有大佬看懂所以提点了我一波根据题上给的数据,模拟一个环出来,再处理出来一个类似前缀和的东西(s[i][j]来表示从i到j区间内奇数数量),把这些搞完之后就是神奇的状态转移方程了,f[i][j]表示从i到j先手取所能取得的最多奇数个数,那么原题解中的状态转移方程是这个样子的
f[i][j]=s[i][j]-min(f[i+1][j],f[i][j-1]);
我蒙了就,搞不懂,那么他的答案统计方式是这样的
if(s[k+1][k+n]-f[k+1][k+n-1]>f[k+1][k+n-1])
ans++;
我惊了这是个啥,整了好久也不懂,然后派出外交官,他搞懂之后再把我搞懂那么改过之后的状态转移方程是这样的
f[i][j]=max(s[i][j]-f[i+1][j],s[i][j]-f[i][j-1]);
虽然长了很多但比之前要好理解了,因为f[i][j]表示的是当前先手,f[i+1][j]和f[i][j-1]是表示上一步的先手,也就是说,他们表示的是对手所取走的,并不是我们的得分,搞懂这点很重要,我们的先手得分是由上一步推来的,也就是对手的先手,所以这里为什么用s减去就很好理解了
然后就是神奇的答案统计方式了,把上一段搞懂这个应该也就没问题了,判断条件转化成人话来说就是,如果对手取的奇数的数量是这个区间的一半,那么你就凉了,反之你就赢了,不再多说
~
1 #include<bits/stdc++.h> 2 using namespace std; 3 int a[305]; 4 int we[305]; 5 int f[305][305]; 6 int ans=0; 7 int main() 8 { 9 int n; 10 scanf("%d",&n); 11 for(int i=1;i<=n;i++) 12 { 13 int op; 14 scanf("%d",&op); 15 we[i]=we[i-1]+(op%2); 16 } 17 for(int i=n+1;i<=2*n;i++) 18 { 19 we[i]=we[i-1]+(we[i-n]-we[i-n-1]); 20 } 21 for(int fi=1;fi<=n;fi++) 22 { 23 memset(f,0,sizeof(f)); 24 for(int i=1;i<=2*n;i++) 25 { 26 f[i][i]=(we[i]-we[i-1]); 27 } 28 for(int i=fi+n-1;i>=fi+1;i--) 29 { 30 for(int j=i+1;j<=fi+n-1;j++) 31 { 32 f[i][j]=(we[j]-we[i-1])-min(f[i+1][j],f[i][j-1]); 33 } 34 } 35 if((we[fi+n]-we[fi])>2*f[fi+1][fi+n-1]) 36 ans++; 37 } 38 cout<<ans; 39 return 0; 40 }
终
为什么一遇到博弈论我就蒙了???
T2(删除)
初
。。。
复
What?竟然是道模拟?话不多说,开始!
1. 需要把第一行出现过且第二、三行没有出现的数在第一行删去;
2. 需要把第二、三行出现的且第一行已经删去的数删去。
3. 在删去某列的数时,需要将第二、三行删去的且数量为0(删后)的数在第一行删去。
只要处理好这几点,应该就离AC不远了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n; 4 struct op 5 { 6 int c,f,z; 7 }a[100005]; 8 int fu1[100005],fu2[100005],ui[100005],ans=0; 9 bool pd[100005],gjyz=false,fi[100005]; 10 inline void cz(int opop) 11 { 12 ans++; 13 fu1[a[opop].f]--; 14 fu2[a[opop].z]--; 15 if(fu1[a[opop].f]==0&&fi[ui[a[opop].f]]) 16 { 17 fi[ui[a[opop].f]]=false; 18 pd[a[opop].f]=false; 19 cz(ui[a[opop].f]); 20 } 21 if(fu2[a[opop].z]==0&&fi[ui[a[opop].z]]) 22 { 23 fi[ui[a[opop].z]]=false; 24 pd[a[opop].z]=false; 25 cz(ui[a[opop].z]); 26 } 27 } 28 inline int read() 29 { 30 31 int x=0; 32 char ch=getchar(); 33 while(ch<'0'||ch>'9') 34 { 35 ch=getchar(); 36 } 37 while(ch>='0'&&ch<='9') 38 { 39 x=(x<<1)+(x<<3)+ch-'0'; 40 ch=getchar(); 41 } 42 return x; 43 } 44 int main() 45 { 46 memset(pd,true,sizeof(pd)); 47 memset(fi,true,sizeof(fi)); 48 scanf("%d",&n); 49 for(int i=1;i<=n;i++) 50 { 51 a[i].c=read(); 52 ui[a[i].c]=i; 53 } 54 for(int i=1;i<=n;i++) 55 { 56 a[i].f=read(); 57 fu1[a[i].f]++; 58 } 59 for(int i=1;i<=n;i++) 60 { 61 a[i].z=read(); 62 fu2[a[i].z]++; 63 } 64 while(gjyz==false) 65 { 66 gjyz=true; 67 for(int i=1;i<=n;i++) 68 { 69 if(pd[i]) 70 if(fu1[i]==0||fu2[i]==0) 71 { 72 fi[ui[i]]=false;pd[i]=false; 73 cz(ui[i]); 74 gjyz=false; 75 } 76 } 77 for(int i=1;i<=n;i++) 78 { 79 if(fi[i]) 80 if(!pd[a[i].f]||!pd[a[i].z]) 81 { 82 fi[i]=false; 83 cz(i); 84 gjyz=false; 85 } 86 } 87 } 88 cout<<ans; 89 return 0; 90 }
终
模拟我居然没有做出来,还是要多想。
T3(区间)
初
是DP?
复
竟然是最长不上升子序列。
不会怎么办?还好OE大佬切了这题。粘。
~
昨天的最后一题,本来昨天就应该解决的但世事难料,整了一晚上只把前三题整完了这题就剩下了,刚询问LXL大佬(同届的未来国家队主力新生代选手以及五学科奥赛金牌获得者)后终于搞懂题意,那么先看一个样例数据
输入2:
5
10 30
20 40
30 50
10 60
30 40
输出2:
3
我们再来看这张极丑陋的图
(自行把坐标*10看)
这就是题上所给的区间了,那么题意是啥呢,就是要求出在大包含小的条件下,最多能有几层,什么意思呢,拿样例来说,(1,6)区间包含了(1,3),那么这个组合就有两层,(1,3)没有包含其他的区间,那么这个组合的层数就是2;同时(1,6)还包含了(2,4)这个区间,(2,4)又包含了(3,4)这个区间,所以总的来说这个组合有三层,剩下的的同理就可以推得,最终最多的层数仍是3,那么答案就是3
那要怎么转化为代码呢?
其实就是求一个多关键字排过序的最长不上升子序列的问题,由于N的范围比较大,所以直接N^2的方法会爆,就需要一个O(n logn)方法了,昨天的时间紧就只粘了个代码在博客,没有灵魂,在理解了题后我又把那个代码改动了一下,(STL牛逼)
~
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n; 4 int read() 5 { 6 int x=0; 7 char ch=getchar(); 8 while(ch<'0'||ch>'9') 9 { 10 ch=getchar(); 11 } 12 while(ch>='0'&&ch<='9') 13 { 14 x=(x<<1)+(x<<3)+ch-'0'; 15 ch=getchar(); 16 } 17 return x; 18 } 19 long long a[100005],f[100005]; 20 struct aa 21 { 22 int op,ed; 23 }oi[100005]; 24 long long maxx(int jk,long long kl) 25 { 26 return (long long)jk>kl?jk:kl; 27 } 28 bool cmp(aa jk,aa kl) 29 { 30 return (jk.op<kl.op)||((jk.op==kl.op)&&(jk.ed>kl.ed)); 31 } 32 long long ans=0; 33 int find(int i,int j,int x) 34 { 35 while(i<=j) 36 { 37 int mid=i+(j-i)/2; 38 i=a[mid]>=x?mid+1:i; 39 j=a[mid]<x?mid-1:j; 40 } 41 return i; 42 } 43 int main() 44 { 45 scanf("%d",&n); 46 memset(a,0,sizeof(a)); 47 for(int i=1;i<=n;i++) 48 { 49 oi[i].op=read(),oi[i].ed=read(); 50 } 51 sort(oi+1,oi+1+n,cmp); 52 memset(a,0xfffffff,sizeof(a)); 53 for(int i=1;i<=n;i++) 54 { 55 int j=find(1,i,oi[i].ed); 56 f[i]=j; 57 a[f[i]]=maxx(oi[i].ed,a[f[i]]); 58 ans=max(f[i],ans); 59 } 60 cout<<ans; 61 return 0; 62 }
终
继续努力!