https://ac.nowcoder.com/acm/contest/3005#question
比赛里感觉比前几天难?也许吧。又是经典2小时理论...后面墨迹出一题。
补题发现还是有可做的题没出?
或许是题解写的太好,实际上我想不到嘻嘻。
记录一下补题和思维。
B.括号序列 栈模拟即可。
A.欧几里得 斐波那契?我感觉我是找规律做的,就加上去就行了。不过我还是做完了B才想到的A的做法的。
C.子段乘积 叫啥尺取来着,经典按K尺取。这里需要注意特判0有在的区间,这里白WA了两发,有点急,发现没特判0有点晚,0的逆元还是0,左右更新的时候都会有影响。
我的做法是,标记0出现的位置,尺取更新的时候只 * 右边的非0,* 左边的非0逆元,同时特判离上个0的位置距离是否满足k。
D.子段异或 刚开始无从下手,以为要止步于此。后来沉思了十几分钟,又看了标签有前缀和,就感觉 区间异或和 的性质实际上有点像区间加减法。就是你一段的异或,和前缀的一段异或如果相同,那么剪掉前缀(实际上是再和前缀做一遍异或,异或自己就是0,就没了),剩下的部分就是异或和为0的一段。
然后分别维护个前缀和,map记录统计一下就过了,
E.最小表达式 看了一眼,虽然很裸,但没看清不存在0,是1~9的数字,感觉不太好码,就去看了F,后来发现大家E都过得很快,那我也做E。发现不存在0,无需特判。
贪心分配数字即可,然后我还模拟了string数字的加法,实际上似乎没有必要,不过我写起来倒还挺快的,越来越熟练了哈哈哈。
实际上直接从低位几个数字加起来,维护进位,一次性搞定就可以了。不过我想的是从高位分配的小的数字开始的,思维就固化成了字符串加法。可以注意一下。
F.树上博弈 看到这个,感觉又止步于此。树上的题,有一说一做的真的少,看到树就慌。玩了样例开始以为距离为2的点对的数量,目的就是把牛妹赶到叶子上动不了。
还画了第三个样例30个点的图,差不多确认就是树上距离点对,立马找点分治板子改。。。然后改不来,以为止步于此,但我又感觉按层数统计就行了,用不着点分治啊。后来过了题的同学的提示,是距离为偶数的点对的数量。
按层数统计过了。不过T了好多发,由于我的统计复杂度应该过大(每个点向下找能不大伐)。搞了2个小时似乎。
点分治似乎更加实用于树上带边权的图?不带边权按层数直接搞距离就行了。
实际上题解的做法,因为奇数+奇数=偶数,偶数+偶数=偶数,树上距离为偶数的点对,就是层数为奇数的点的数量,和偶数的点的数量内部各自的 排列 A (n,2)即可。
然后树上距离为奇数的点对数量你也会了吧。
由于这题树的边给的方式十分的特别,下次看到应当注意,甚至不需要建边dfs,省了O(n)的一遍dfs统计层数,就可以算出每个点所在的层数。
G.音乐鉴赏
概率期望题,感觉自己也挺弱的,每次都是赛中懵逼题,赛后傻逼题。好像有点夸张,反正赛中很少做出来就是了。
这题告诉我们一个道理:大胆设出来,此处@ajj。
有两种做法:
1.二分,实际上更好想,似乎不需要推式子就可以上手码,没往这里想,我有问题,事实上这种正面比较难求,给定答案就能够反向check的题,应该往这里想的。这里check就是算在这个p下,最终的优秀率的期望与目标10%的差异。 而最终的优秀率的期望 -> 就是优秀的人数的期望 / 总人数 -> 优秀人数的期望就是每个人优秀概率的和 ->
每个人的优秀概率怎么求? 你优秀所需要的随机分数 y= (90 - score*(1-p))/p,你需要随机分数>=y,那你的优秀概率就是 1 - ( (90 - score*(1-p))/p ) /90,因为随机分数均匀分布嘛,1- (<=y的分数概率) 就是 >=y 的分数的概率。
然后就结束了。就贴个二分的代码吧。
1 #include2 #ifndef ONLINE_JUDGE 3 #define debug(x) cout << #x << ": " << x << endl 4 #else 5 #define debug(x) 6 #endif 7 using namespace std; 8 typedef long long ll; 9 const int maxn=2e5+7; 10 const int inf=0x3f3f3f3f; 11 const int mod=1e9+7; 12 13 int n,a[maxn]; 14 15 double check(double p) 16 { 17 double sum=0; 18 for(int i=0;i i) 19 { 20 double need = (90-a[i]*(1-p))/p; 21 sum += 1-need/90; 22 } 23 return sum/n; 24 } 25 int main() 26 { 27 scanf("%d",&n); 28 for(int i=0;i "%d",&a[i]); 29 double l=0,r=1; 30 double ans; 31 for(int i=0;i<100;++i) 32 { 33 double m=(l+r)/2; 34 if(check(m)>0.1) 35 { 36 l=m; 37 ans=m; 38 } 39 else r=m; 40 } 41 printf("%.2f%\n",100*ans); 42 return 0; 43 }
2.直接正面推,需要设变量(实际上跟我们做数学题一样,就是需要用形式化的语言去解,而不是空想,比如我~),我这里码一下 就是 每个人优秀的概率是啥?
一个人优秀的式子(好有喜感): (score)*(1-p)+y*(p)>=90, 也就是 y >= (90-(score)*(1-p)) (score代表他的平时分,y代表随机到的论文分)
其实跟上面那个式子一样 ,也就是y>=这个式子的概率是啥? 就是这个玩意儿: 1 - ( (90 - score*(1-p))/p ) /90,就是每个人的优秀率,
最后化简,对每个人求和,就会出来能够O(1)出答案的式子了。
希望自己记住这种概率期望题要敢于设变量,建式子想?
H.坐火车
比赛了看了几眼,树状数组啊,啥?要相同颜色的车厢数乘起来?
我只能想到,对每个车厢的每个l,r区间需要分别query然后乘起来,这不是复杂度爆炸嘛。不会做。。。然后就滚去想G了。
实际上如果再深入思考下,发现从一个车厢到下一个车厢,改变的东西十分的有限,就上个的col和当前的col而已。这样的转移变化十分的精简。
树状数组维护每个颜色的组合对数,然后从左到右每个车厢减去当前颜色在左边的总数量,(因为当前这个点颜色的对左边的贡献全没了),然后加上这个当前颜色在右边的总数量(因为当前这个点的颜色对右边的组合的贡献需要加上),同时维护左右区间各种当前颜色的数量即可。
下次见希望会做,主要是这个转移,需要深入想下,每次从上一次来的变化。
1 #include2 #ifndef ONLINE_JUDGE 3 #define debug(x) cout << #x << ": " << x << endl 4 #else 5 #define debug(x) 6 #endif 7 using namespace std; 8 typedef long long ll; 9 const int maxn=1e6+7; 10 const int inf=0x3f3f3f3f; 11 const int mod=1e9+7; 12 13 struct BIT 14 { 15 ll sum[maxn]; 16 void clear(){memset(sum,0,sizeof sum);} 17 int lowbit(int x){return x & (-x);} 18 void add(int x,int val) 19 { 20 while (x < maxn) 21 { 22 sum[x] += val; 23 x += lowbit(x); 24 } 25 } 26 ll query(int x) 27 { 28 if (x <= 0) return 0; 29 ll res = 0; 30 while (x) 31 { 32 res += sum[x]; 33 x -= lowbit(x); 34 } 35 return res; 36 } 37 ll query(int l,int r){return query(r) - query(l -1);} 38 }bit; 39 40 41 int col[maxn],l[maxn],r[maxn]; 42 43 int rsum[maxn],lsum[maxn]; 44 int main() 45 { 46 ios::sync_with_stdio(false); 47 cin.tie(0); 48 int n; 49 cin>>n; 50 for(int i=1;i<=n;++i) 51 { 52 cin>>col[i]>>l[i]>>r[i]; 53 rsum[col[i]]++; 54 } 55 for(int i=1;i<=n;++i) 56 { 57 rsum[col[i]]--; 58 bit.add(col[i],-lsum[col[i]]); 59 cout< ' '; 60 lsum[col[i]]++; 61 bit.add(col[i],rsum[col[i]]); 62 } 63 cout<<endl; 64 return 0; 65 }
I.匹配星星
比赛里看了,以为这是三维偏序经典题?CDQ分治???翻了以前做的,然后发现题目不一样,然后不会做。
实际上完全没注意到范围,z是0~1,然后就变成了贪心傻逼题了。
按其中一维,比如x,sort一下,按x循环的顺序搞,z=0是我们的目标群体,z=1去找配对,显然找比当前y小的最大的一个y最好,因为要把小的y让给别人呀,找到要删除。
于是用类set的multiset维护,因为y可能会重嘛,每次lowerbound二分查一下即可。
这个题解里讲的很清楚,就不赘述(逃~)。
https://ac.nowcoder.com/discuss/365889?type=101&order=0&pos=8&page=3
1 #include2 #ifndef ONLINE_JUDGE 3 #define debug(x) cout << #x << ": " << x << endl 4 #else 5 #define debug(x) 6 #endif 7 using namespace std; 8 typedef long long ll; 9 const int maxn=2e5+7; 10 const int inf=0x3f3f3f3f; 11 const int mod=1e9+7; 12 13 struct point 14 { 15 int x,y,z; 16 }p[maxn]; 17 bool cmp(point a,point b) 18 { 19 return a.x<b.x; 20 } 21 22 int main() 23 { 24 ios::sync_with_stdio(false); 25 cin.tie(0); 26 int n; 27 cin>>n; 28 for(int i=0;i >p[i].x>>p[i].y>>p[i].z; 29 sort(p,p+n,cmp); 30 multiset<int>st; 31 multiset<int>::iterator it; 32 int ans=0; 33 for(int i=0;i i) 34 { 35 if(p[i].z==1) 36 { 37 it=st.lower_bound(p[i].y); 38 if(it==st.begin()) continue; 39 ans++; 40 it--; 41 st.erase(it); 42 } 43 else 44 st.insert(p[i].y); 45 } 46 cout< endl; 47 return 0; 48 }
J.二维跑步
比赛里完全没看,第一次没人ak的场的压轴题?感觉确实挺难的一个计数题。
看题解,前面的都还好,虽然很牛逼,但可以理解。就最后一段,没懂 错位加法是啥,目前还没搞懂。以后也不一定搞懂。。。代码循环内最后一部分不知道在干啥。
他写的取模运算函数感觉还不错噢。
逃~