模拟
题目链接:https://www.luogu.org/problem/show?pid=1311
又一道好玩的题目~~
放一下洛谷的图:
看完题后最直接的想法是先找颜色相同的客栈,
然后再找两两之间是否有小于等于s的客栈,
统计答案,
具体怎么实现呢?
一边输入我就能处理出颜色相同的客栈,
因为我要求每两个颜色相同的客栈之间的是否有小于等于p的客栈,
所以要顺序遍历所有颜色相同的客栈,
什么东西可以方便的查询他的上一个元素和下一个元素?
链表!(数组!)
pre[],h[]分别代表上一个元素和最后一个元素
大概是这样:
pre[i] = h[x];
h[x] = i;
(链表没有好好学QAQQQ)
假设我们已经找到了所有颜色相同的客栈,
然后,
要找颜色相同的客栈之间,
是否有消费小于等于p的客栈?
那我直接找两客栈之间的最小值,
怎么快速查询最小值?
st表!
Codes:
1 struct st{
2 void init(){
3 for(int i = 1;(1 << i) <= n;++ i){
4 for(int j = 1;j + (1 << i) - 1 <= n;++ j){
5 dp[j][i] = minn(dp[j][i - 1],dp[j + (1 << (i - 1))][i - 1]);
6 }
7 }
8 }
9 int que(int l,int r){
10 int k = log2(r - l + 1);
11 return minn(dp[l][k],dp[r - (1 << k) + 1][k]);
12 }
13 }st;
60分思路出来了
Codes:
1 #include<iostream>
2 #include<cstring>
3 #include<cstdio>
4 #include<cmath>
5 #include<algorithm>
6 using namespace std;
7 const int N = 200000 + 5;
8 bool bo[N];
9 int k,s,h[N],ans,cnt,kind[N];
10 int n,nxt[N],pre[N],dp[N][25];
11 int minn(int x,int y){if(x < y) return x;return y;}
12 struct st{
13 void init(){
14 for(int i = 1;(1 << i) <= n;++ i){
15 for(int j = 1;j + (1 << i) - 1 <= n;++ j){
16 dp[j][i] = minn(dp[j][i - 1],dp[j + (1 << (i - 1))][i - 1]);
17 }
18 }
19 }
20 int que(int l,int r){
21 int k = log2(r - l + 1);
22 return minn(dp[l][k],dp[r - (1 << k) + 1][k]);
23 }
24 }st;
25 int main(){
26 int cnt = 0,x;
27 scanf("%d%d%d",&n,&k,&s);
28 for(int i = 1;i <= n;++ i){
29 scanf("%d%d",&x,&dp[i][0]);
30 if(!bo[x]){
31 kind[++ cnt] = x;
32 bo[x] = 1;
33 }
34 pre[i] = h[x];
35 h[x] = i;
36 }
37 st.init();
38 for(int i = 1;i <= k;++ i){//60 T4个点
39 for(int j = h[kind[i]];j;j = pre[j]){
40 for(int x = pre[j];x;x = pre[x]){
41 int xx = x,yy = j;
42 int t = st.que(xx,yy);
43 if(t <= s)
44 ans ++;
45 }
46 }
47 }
48 cout << ans << '\n';
49 return 0;
50 }
仔细想想,其实38行到40行的for是有重复部分的
时间就大大大大的浪费在了只跑到那些点却不记录的过程中,
来自57某大神的想法(orzorz):
如果我们已经知道这个位置的客栈和在他之前的客栈x之间
有小于等于p的客栈,
那么在x之前的客栈和这个客栈之间一定有可行方案,
所以开一个数组tot[]
tot[k]:记录之前颜色为k的客栈出现的次数
一个数组sum[]
sum[k]:记录之前已找到的“小于花费”的颜色为k的客栈出现次数
是不是有点绕?
记住tot和sum是不一样的不一样的不一样的!
但是在找到小于花费的客栈时,可以用tot来更新sum(上面已经解释过了),
然后考虑优化st表
我们实际上不需要知道两个客栈之间的最小值,
我只要知道最近的小于s的客栈在哪就好啦~
当然也可能是他自己,
这样100就拿到啦~
Codes:
1 #include<iostream>
2 #include<cstring>
3 #include<cstdio>
4 #include<cmath>
5 #include<algorithm>
6 using namespace std;
7 const int N = 200000 + 5;
8 int k,s,h[N],ans,now;
9 int sum[N],tot[N];
10 int n;
11 int main(){
12 int x,y;
13 scanf("%d%d%d",&n,&k,&s);
14 for(int i = 1;i <= n;++ i){
15 scanf("%d%d",&x,&y);
16 if(y <= s)
17 now = i;
18 if(now >= h[x])
19 sum[x] = tot[x];
20 h[x] = i;
21 tot[x] ++;
22 ans += sum[x];
23 }
24 cout << ans << '\n';
25 return 0;
26 }
27 /*
28 tot[k]:记录之前颜色为k的客栈出现的次数;
29 sum[k]:记录之前“已找到小于花费”的颜色为k的客栈出现次数;
30 h[k]:当前颜色为k的客栈最后出现的位置;*/
挺好玩的2333
当时讲的超级仔细~~rjj好负责的
这个可以学习~