写一下把我吊着打的离线赛总结 (?)
\(update\) \(2020.10.3\)
学会的东西:
struct p30 { }p30;
什么的 (就可以完美地水暴力了
然后就50pts暴力写错了 胡的正解是对的23333
丽江河边有 n
家很有特色的客栈,客栈按照其位置顺序从 1
到 n
编号。每家客栈都按照某一种色调进行装饰(总共 k
种,用整数 0∼k−1
表示),且每家客栈都设有一家咖啡店,每家咖啡店均有各自的最低消费。
两位游客一起去丽江旅游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定分别住在色调相同的两家客栈中。晚上,他们打算选择一家咖啡店喝咖啡,要求咖啡店位于两人住的两家客栈之间(包括他们住的客栈),且咖啡店的最低消费不超过 p
。
他们想知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费不超过 p
元的咖啡店小聚。
输入格式
共 n+1
行。
第一行三个整数 n, k, p
,每两个整数之间用一个空格隔开,分别表示客栈的个数,色调的数目和能接受的最低消费的最高值;
接下来的 n
行,第 i+1
行两个整数,之间用一个空格隔开,分别表示 i
号客栈的装饰色调 a[i]
和 i
号客栈的咖啡店的最低消费 b[i]
。
被欺骗了的50pts
其实k的范围并不大 刚好不会爆空间
然后暴力就错了。 错了。
#include <bits/stdc++.h> using namespace std; #define ll long long const int N= 200005, N2= 1005, K= 55; int t[N], w[N]; ll cnt[N][K]; int M[N2][N2]; int n, k, p; int ne[N]; void init() //预处理出的ne数组表示最前面可以放到哪 { memset(ne, -1, sizeof ne); for(int i=1; i<=n; i++) if(w[i]<= p) ne[i]= i; for(int i=n- 1; i>=1; i--) { if(ne[i]== i) continue; ne[i]= ne[i+ 1]; } return ; } struct p50 { void get() { memset(M, 0x3f, sizeof M); for(int i=1; i<=n; i++) for(int j=i+ 1; j<=n; j++) M[i][j]= min(M[i][j- 1], w[j]); //是这里错了啊 应该是for(int j=i; j<=n; j++) 事实证明大样例拉胯 return ; } void solve() { get(); ll cnt= 0; for(int i=1; i<=n; i++) for(int j=i+ 1; j<=n; j++) if(t[i]== t[j]&& M[i][j]<= p) cnt++ ; printf("%lld\n", cnt); exit(0); } }p50; int main() { // freopen("hotel.in", "r", stdin); // freopen("hotel.out", "w", stdout); cin>> n>> k>> p; for(int i=1; i<=n; i++) scanf("%d%d", &t[i], &w[i]); // if(n<= 1000) p50.solve(); //被注释掉就AC的东西 for(int i=1; i<=n; i++) cnt[i][t[i]]++ ; for(int i=1; i<=n; i++) for(int j=0; j<k; j++) cnt[i][j]+= cnt[i- 1][j]; //cnt[i][j]表示第i个位置之前的颜色j有多少种 ll ans= 0; init(); for(int i=1; i<=n; i++) { if(ne[i]== -1) continue; int j= ne[i]; ans+= (ll)cnt[n][t[i]]- cnt[j- 1][t[i]]; if(i== j&& (ll)cnt[n][t[i]]- cnt[j- 1][t[i]]!= 0) ans-- ; //减去自身 当然不用特判后面那个条件 留下来做纪念qaq } cout<< ans<< endl; return 0; }
赛后才发现我写得非常垃圾qaq
有一个加强版加强了k
的范围 就是卡这个算法的
其实可以记last
数组 枚举第二个
#include <bits/stdc++.h> using namespace std; #define ll long long const int N= 2* 1e6+ 5, K= 1e4+ 5; int t[N], w[N], cnt[K], last[N]; int main() { int n, k, p; cin>> n>> k>> p; for(int i=1; i<=n; i++) scanf("%d%d", &t[i], &w[i]); memset(last, -1, sizeof last); for(int i=1; i<=n; i++) { if(w[i]<= p) last[i]= i; else last[i]= last[i- 1]; } int pre= 0; ll ans= 0; for(int i=1; i<=n; i++) { if(last[i]== -1) continue; for(int j=pre+ 1; j<=last[i]; j++) cnt[t[j]]++ ; ans+= cnt[t[i]]- (last[i]== i); pre= last[i]; } cout<< ans<< endl; return 0; }
当然也有更简单的 把last
求的过程放在循环里面 因为好像优化不了什么就不写了
题外话:老师说这道题没A的同学好好想想这几年学了些什么23333
没完整版题目和代码谅解
题目大概就是说给定一个长度为n
的序列, q
次询问, 每次询问一个l[i]
, r[i]
问区间中有没有重复的数
数据范围好像卡掉了 \(O(nlogn)\) 的东西qaq (关键是我 \(O(nlogn)\) 的也不会啊
被虐爆的一天 只会 \(O(qn)\) 的60pts 好在部分分给得很足
我们可以先离散化, 然后60pts到手, 跑路
正解: 我们可以存一个 pre[i]
表示第i
个前面离第i
个最近的和他相同的数是什么
每次询问就要求 max(pre[l[i]]——pre[r[i]])
中有没有大于等于l[i]
的
这时你就可以打一个st
表 复杂度什么的应该是可以的
再看一下max(pre[l[i]]——pre[r[i]])
可以转换成max(pre[1]——pre[r[i]])
然后就是前缀和了
然后这题最难的其实是排序, 需要 \(O(n)\) 的排序, 数的范围1e9
基数排序什么的手写一下就好了。https://www.luogu.com.cn/blog/lqt121720/ji-shuo-pai-xu
[https://www.luogu.com.cn/problem/P1314]([NOIP2011 提高组] 聪明的质监员)
明显(?)的二分题
然后被我搞成了三分23333
大概
while(l< r) { int mid= (l+ r+ 1)>> 1; if(check(mid)>= check(mid+ 1)) l= mid; else r= mid- 1; }
这种
发现三分只能求答案可以多个但是不是答案的一定要是一个的东西 (错了拍醒我 我们这道题是不可能的满足的
所以我们可以二分求出一个小于等于s
的最大的
#include <bits/stdc++.h> using namespace std; #define int long long const int N= 200005; int w[N], v[N]; int cnt[N], val[N]; int l1[N], r1[N]; int n, m; int s; int maxn= 0; int check(int x) { memset(cnt, 0, sizeof cnt); memset(val, 0, sizeof val); for(int i=1; i<=n; i++) { if(w[i]>= x) cnt[i]++ , val[i]+= v[i]; } for(int i=1; i<=n; i++) cnt[i]+= cnt[i- 1], val[i]+= val[i- 1]; int ans= 0; for(int i=1; i<=m; i++) ans+= (int)(cnt[r1[i]]- cnt[l1[i]- 1])* (val[r1[i]]- val[l1[i]- 1]); return ans; } signed main() { cin>> n>> m>> s; for(int i=1; i<=n; i++) { scanf("%lld%lld", &w[i], &v[i]); maxn= max(maxn, w[i]); } for(int i=1; i<=m; i++) scanf("%lld%lld", &l1[i], &r1[i]); int l= 0, r= maxn+ 1; int ans= (int)1e18; while(l< r) { int mid= (l+ r)>> 1; int x= check(mid); ans= min(ans, abs(x- s)); if(x> s) l= mid+ 1; else r= mid; } cout<< ans<< endl; return 0; }