【离线做法】可持久化的书橱

相信那些数据结构题做多的大佬,在看完题之后立马用主席树切了此题

题目描述

Verland 是一个神奇的星球,蒟蒻妹子进入宫殿之后发现了一个 N 层,每层有 M 个格子的神奇书橱,她可以对这个书橱做如下操作:

  • 1 i j:在第 i 层的第 j 格放入一本书(如果之前有则这次操作无效)
  • 2 i j :将放在第 i 层的第 j 格的书取走(如果之前没有则这次操作无效)
  • 3 i:将第 i 层的格子翻转,有书的格子变成没有,没有的变成有
  • 4 i:返回第 i 次操作后书橱的状态

蒟蒻妹子想知道每次操作后书橱上的书的总数。

由于蒟蒻妹子觉得数数很累,于是她想让你帮助她解决这个问题.

输入格式

第 1 行三个整数 N,M,Q

接下来 Q 行 第一个数表示操作类型,接下来的输入如题意所说

输出格式

总共 Q 行,每行一个整数,表示这次询问的答案

数据规模与约定

保证数据合法

Subtask 1 (27):N1000,M1000,Q100000,操作 4 的次数4

Subtask 2 (25):N100,M100,Q100

Subtask 3 (13):N=1000,M=1000,Q=300000

Subtask 4 (13):N=1000,M=1000,Q=300000

Subtask 5 (13):N=1000,M=1000,Q=300000

时间限制:1s

空间限制:512MB


题目分析

做法一:强行模拟可持久化

记录每一个要被返回到的状态,返回时直接赋值上即可。

处理细节例如操作怎么处理没有什么好说的。

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int MAX_HISTORY = 4103;
 4 int n,m,q,ans[300035],k[1003],tot;
 5 int lb[300035];
 6 struct node
 7 {
 8     int tt,x,y;
 9 }s[300035];
10 bitset<1003> f[1003];
11 bitset<1003> sv[MAX_HISTORY][1003];
12 int his[MAX_HISTORY][1003];
13 int read()
14 {
15     char ch = getchar();int num = 0;
16     for (; !isdigit(ch); ch = getchar());
17     for (; isdigit(ch); ch = getchar())
18         num = (num<<1)+(num<<3)+ch-48;
19     return num;
20 }
21 int main()
22 {
23     n = read();m = read();q = read();
24     for (int i=1; i<=q; i++)
25     {
26         s[i].tt = read();s[i].x = read();
27         if (s[i].tt <= 2)s[i].y = read();
28         if (s[i].tt==4)if (!lb[s[i].x])lb[s[i].x] = ++tot;
29     }
30     register int i,j;
31     for (i=1; i<=q; i++)
32     {
33         int ansk = ans[i-1],tt = s[i].tt,x = s[i].x,y = s[i].y;
34         if (tt==1){
35             if (!f[x][y]){
36                 f[x][y] = 1;
37                 k[x]++;ansk++;
38             }
39         }else if (tt==2){
40             if (f[x][y]){
41                 f[x][y] = 0;
42                 k[x]--;ansk--;
43             }
44         }else if (tt==3){
45             f[x].flip();
46             ansk += m-2*k[x];
47             k[x] = m-k[x];
48         }else if (tt==4){
49             ansk = ans[x];
50             for (int j=1; j<=n; j++)
51                 f[j] = sv[lb[x]][j],k[j]=his[lb[x]][j];
52         }
53         if (lb[i]){for (j=1; j<=n; j++)sv[lb[i]][j] = f[j],his[lb[i]][j]=k[j];}
54         printf("%d\n",ansk);
55         ans[i] = ansk;
56     }
57     return 0;
58 } 

 

加上bitset乱搞可过52分。

 

做法二:典型离线题

不一定非得拘泥于「可持久化」的题目名字。

站在另一种角度想一想:把每一个状态看做是一个节点,节点与节点之间的转移就是不同的操作。这样一来,根本无需存储需要返回的状态,而是在这样一张新的图上dfs回溯就可以了。

那么这就是一道典型的离线题。

举个例子,假设现在没有4号操作,那么整张图操作顺序可以看成这样:

再而,按照每一个节点操作下去就可以了。

又如果有了4号操作,操作顺序可以表示成这样:

 

其中5号节点的操作是返回2号节点;7号节点操作是返回5号节点。

值得注意的是,有可能返回0号节点(初始状态)

那么只要dfs从0开始就可以了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cctype>
 4 #include<vector>
 5 #include<bitset>
 6 
 7 struct node
 8 {
 9     int tt,x,y;
10 }a[300035];
11 struct scenario
12 {
13     std::bitset<1003> f[1003];
14     bool fpd;
15     int k[1003];
16 }f;
17 int n,m,q,ans[300035];
18 std::vector<int> otd[300035];
19 
20 void dfs(int now)
21 {
22     if (now > q) return;
23     int i = a[now].x, j = a[now].y, delta = 0;
24     if (a[now].tt==1)
25         if (!f.f[i][j]){
26             delta++;
27             f.k[i]++;
28             f.f[i][j] = 1;
29         }
30     if (a[now].tt==2)
31         if (f.f[i][j]){
32             delta--;
33             f.k[i]--;
34             f.f[i][j] = 0;
35         }
36     if (a[now].tt==3)
37     {
38         delta = m-2*f.k[i];
39         f.k[i] = m-f.k[i];
40         f.f[i].flip();
41     }
42     ans[now] += delta;
43     for (unsigned int i=0; i<otd[now].size(); i++)
44     {
45         ans[otd[now][i]] = ans[now];
46         dfs(otd[now][i]);
47     }
48     i = a[now].x, j = a[now].y;
49     if (a[now].tt==1 && delta==1)
50     {
51         f.k[i]--;
52         f.f[i][j] = 0;
53     }
54     if (a[now].tt==2 && delta==-1)
55     {
56         f.k[i]++;
57         f.f[i][j] = 1;
58     }
59     if (a[now].tt==3)
60     {
61         f.k[i] = m-f.k[i];
62         f.f[i].flip();
63     }
64 }
65 int read()
66 {
67     char ch = getchar();int num = 0;
68     for (; !isdigit(ch); ch = getchar());
69     for (; isdigit(ch); ch = getchar())
70         num = (num<<1)+(num<<3)+ch-48;
71     return num;
72 }
73 int main()
74 {
75     n = read();
76     m = read();
77     q = read();
78     otd[0].push_back(1);
79     for (int i=1; i<=q; i++)
80     {
81         if ((a[i].tt = read()) <= 2)
82             a[i].x = read(),a[i].y = read();
83         else a[i].x = read();
84         if (a[i].tt!=4) otd[i-1].push_back(i);
85         else otd[a[i].x].push_back(i);
86     }
87     dfs(0);
88     for (int i=1; i<=q; i++)
89         printf("%d\n",ans[i]);
90     return 0;
91 }

 

 做法三:主席树

 

 

END

 

posted @ 2018-04-17 08:47  AntiQuality  阅读(203)  评论(0编辑  收藏  举报