自己出题:妲丽安想记住

题目

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);
	}
}
posted @ 2021-11-21 18:11  white514  阅读(101)  评论(3编辑  收藏  举报