自己出题:妲丽安想记住
题目
timelimit: 1s
memerylimit: 256M
描述:
天空于我过于宽阔,时光于我过于短暂。
在无知中空虚,连汇集的话语,都在黄土中朽去。——《丹特丽安的书架》
妲丽安是一位爱读书的哥特装萝莉。她的某个书架上有\(n\)本书,一开始她记住了这\(n\)本书中所有的内容,但是如果她有一天没重温一本书的话,她就会忘记这本书内容的\(\frac{1}{k}\),也就是说\(k\)天之后她就会把这本书的内容完全忘记。如果她某天重温这本书,那么她又会记住这本书的全部内容。
她现在正忙于阅读其他书架上的书,因此每天只能阅读这\(n\)本书中的一本,或是查询从第\(l\)本书到第\(r\)本书中有多少书的内容她已经全部忘记了(但是这天并不会看这\(n\)本书)。
输入格式
第一行有三个整数\(n,k,m(1\le n,k,m \le 10^5)\),表示这个暑假上书的数量、需要多少天忘记一本书的内容、询问的天数。
接下来\(m\)行,每行第一个整数是\(p(p\in\{1,2\} )\),
如果\(p=1\),那么后面有一个整数\(x(1\le x\le n)\),表示这一天妲丽安重温了第\(x\)本书;
如果\(p=2\),那么后面跟两个整数\(l,r(1\le l \le r \le n)\),表示妲丽安想知道从\(l\)到\(r\)有多少本书她已经全部忘光了。
输出格式
输出\(m\)行。如果这一天妲丽安阅读了书籍,请输出这本书在阅读前她还记内容的\(k\)分之多少;如果这一天妲丽安在询问,那么请输出询问的结果。
输入样例
5 3 5
1 1
2 1 4
1 3
2 1 5
1 3
输出样例
2
0
0
4
1
说明
每本书记住的内容:
天数\书的编号 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
0 | 3 | 3 | 3 | 3 | 3 |
1 | 2$\to$3 | 2 | 2 | 2 | 2 |
2 | 2 | 1 | 1 | 1 | 1 |
3 | 1 | 0 | 0$\to$3 | 0 | 0 |
4 | 0 | 0 | 2 | 0 | 0 |
5 | 0 | 0 | 1$\to$3 | 0 | 0 |
题解
你需要知道的知识:
1.树状数组(对于有n个元素的数组,可以在O(log(n))次计算内获得任意[l,r]区间的和 )
2.队列(一种数据结构,特性是先进先出)
如果一本书完全忘记,则将它设为1,否则设为0;可以用树状数组查询区间[l,r]中1的书的个数。
用一个last数组记录每本书最近一次被阅读是在哪一天;设置一个长为k的队列Q,记录最近k天阅读了哪本书(如果某一天没有阅读,则设为阅读了第n+1本书);用cnt数组记录一本书在Q中出现了几次。用这三个辅助数组就可以实现对树状数组的修改。
std代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n, m, k;
int a[N], cnt[N], last[N];
queue<int> Q;
int lowbit(int x){ return x&(-x); }
void Updata(int w, int x){
for(; w<=n; w+=lowbit(w)) a[w]+=x;
}
int Query(int w){
int res=0;
for(; w; w-=lowbit(w)) res+=a[w];
return res;
}
int main(){
cin>>n>>k>>m;
//init
for(int i=1; i<=k; ++i) Q.push(n+1);
cnt[n+1]=m+1;
for(int i=1; i<=n; ++i) Updata(i,1), cnt[i]=1;
//solve
for(int i=1, p, x, l, r; i<=m; ++i){
//forget
int u=Q.front(); Q.pop();
if(!(--cnt[u])) Updata(u,-1);
if(i==k) for(int j=1; j<=n; ++j){
if(!last[j]) Updata(j,-1);
cnt[j]--;
}
//input
scanf("%d", &p);
if(p==1){
scanf("%d", &x);
printf("%d\n", max(k+last[x]-i,0));
//review
Q.push(x);
last[x]=i;
if(!(cnt[x]++)) Updata(x,1);
}else{
Q.push(n+1);
scanf("%d%d", &l, &r);
//query
printf("%d\n", (r-l+1)-(Query(r)-Query(l-1)));
}
}
}
测试点信息
一共14个测试点,前10个完全随机,11/12每天都读,13/14每天都询问。
数据生成代码:
#include<bits/stdc++.h>
using namespace std;
mt19937 rnd(time(0));
const int N=1e5;
typedef tuple<int,int,int> TIII;
FILE *fw;
int n, k, m;
TIII Data[N+10];
void fun1(){
//generate
n=N; k=rnd()%N+1; m=N;
for(int i=1; i<=m; ++i){
int p=rnd()&1, l=rnd()%N+1, r=rnd()%N+1;
if(l>r) swap(l,r);
Data[i]=TIII(p+1,l,r);
}
//output
fprintf(fw, "%d %d %d\n", n, k, m);
for(int i=1, p, l, r; i<=m; ++i){
tie(p,l,r)=Data[i];
fprintf(fw, "%d", p);
p==1?fprintf(fw, " %d\n", rnd()&1?l:r):fprintf(fw, " %d %d\n", l, r);
}
}
int main(){
char s[100];
for(int i=1; i<=10; ++i){
sprintf(s, "%d.in", i);
fw=fopen(s,"w");
fun1();
fclose(fw);
}
}