一些趣题的回忆

Posted Doggu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一些趣题的回忆相关的知识,希望对你有一定的参考价值。

  这种题目,常常道听途说,或是考试用题。常常难觅出处,却又非常经典。故选其精华,小列如下。


 T1:fleet 给定一个序列,询问[L,R]间有多少种不同的权值。

e.g.:序列1,1,2,3,2的[1,5]有3种不同权值,[1,3]有2种不同权值。

ANSWER:可以考虑使用主席树求解。查询[L,R]时返回root[R]的[L,R]值之和。root[i]与root[i-1]的不同在于:prev[aa[i]]这个位置(即上一次出现aa[i]的位置)-1,在i这个位置+1。先预处理完毕,再应付查询。


 T2:给定一个序列,询问[L,R]间有多少种权值k恰出现k次。

e.g.:序列1,1,2,3,2的[1,1]有1种权值,[1,2]有0种权值,[2,5]有2种权值。

ANSWER:可以考虑使用主席树求解。但是,我们也可以考虑使用离线。因为答案本质上统计的是一种情形,而我们可以考虑该情形对答案的贡献。如果把询问[L,R]转化为二维矩阵中某点[L,R]的权值,那么每一种情形的贡献也可以看做是矩形的修改。因为这样很令人不爽,可以进一步地转换,使用差分的思想,将矩形拆成4个角,问题于是转化为单点修改与矩阵前缀和的查询。这个东西很像BZOJ的一道题:MONICA。那是一道CDQ分治套树状数组的典型题目,因为有时间的先后。但是,这道题目完全可以先修改再查询,于是可以sort以后直接使用树状数组求解。

 1 #define PN "count"
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 template<class T>inline void readint(T &res) {
 6     static char ch;T flag=1;
 7     while((ch=getchar())<0||ch>9)if(ch==-)flag=-1;
 8     res=ch-48;
 9     while((ch=getchar())>=0&&ch<=9)res=res*10+ch-48;
10     res*=flag;
11 }
12 const int N = 1000000 + 100;
13 const int Q = 1000000 + 100;
14 struct DATUM {
15     int x, y, delta;
16     bool operator<(const DATUM &rhs) const {
17         if(x!=rhs.x) return x<rhs.x;
18         if(y!=rhs.y) return y<rhs.y;
19         if(delta!=rhs.delta) return delta<rhs.delta;
20     }
21 } data[N*4+Q];
22 int tot, ans[Q];
23 inline void addD(int x,int y,int delta) {data[++tot]=(DATUM){x,y,delta};}
24 
25 int n, a[N];
26 void add(int pos,int val) {for(int x=pos;x<=n;x+=x&-x)a[x]+=val;}
27 int query(int pos) {int val=0;for(int x=pos;x;x-=x&-x)val+=a[x];return val;}
28 
29 #include <vector>
30 std::vector<int> same[N];
31 int main() {
32     int q;readint(n);readint(q);
33     for( int i = 1; i <= n; i++ ) same[i].push_back(0);
34     for( int i = 1, x, siz; i <= n; i++ ) {
35         readint(x);same[x].push_back(i);
36         siz=same[x].size();
37         if(siz>x) {
38             addD(same[x][siz-x-1]+1,i,+1);
39             addD(same[x][siz-x]+1,i,-1);
40             if(siz>x+1) addD(same[x][siz-x-2]+1,i,-1); 
41             if(siz>x+1) addD(same[x][siz-x-1]+1,i,+1);
42         }
43     }
44     for( int i = 1, l, r; i <= q; i++ ) readint(l),readint(r),addD(l,r,i+1);
45     std::sort(data+1,data+tot+1);
46     for( int i = 1; i <= tot; i++ ) {
47         if(data[i].delta<=1) add(data[i].y,data[i].delta);
48         else if(data[i].y>=1&&data[i].y<=n) ans[data[i].delta-1]=query(data[i].y);
49     }
50     for( int i = 1; i <= q; i++ ) printf("%d\n",ans[i]);
51     return 0;
52 }

 未完待续……

 

以上是关于一些趣题的回忆的主要内容,如果未能解决你的问题,请参考以下文章

程序员的算法趣题Q22: 不缠绕的纸杯电话

程序员的算法趣题Q66: 设计填字游戏

程序员的算法趣题pdf

趣题[0]

序列相关的趣题 之二

程序员的算法趣题Q14: 国名接龙