您的位置:首页 > 其它

Codeforces Round #368 (Div. 2) D - Persistent Bookcase

2016-08-22 19:34 260 查看
题目大意是对一个n层,每层有m个位置的书架进行4种操作:

1)在位置(i,j)上放一本书,如果已经有书,则不在方书

2)拿走位置(i,j)上的一本书,如果没书,则不能拿走

3)将第i层的所有位置的状态翻转,如果某位子有书,则拿走,如果某位置没书,则放一本书

4)将书架的状态还原到之前的某个状态

比赛的时候这个题目并没有思路,最后看了别人思路。自己写了一下。觉得方法比较巧妙,所以写博客记之,以便以后回顾。题目要求每次操作的后输出当前书架上的书的数量。我们需要将操作看成边,当前的状态看作节点,建树。建树的规则是,如果进行的操作树1或者2或者3,那么就将操作后状态连接在当前状态后面,如果操作是4,就将操作后的状态连接在要还原的状态后面。然后进行dfs,离线求解。dfs的巧妙之处在于,由于数据量比较大,不可能将每次操作后的状态都记下来。其实,仔细想想,根本不需要记录所有的状态,只需要记录当前状态,然后dfs,回溯的时候将更改的状态在改回来。这样,一边dfs就解决问题。时间复杂度为q*m。下面贴上代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>

using namespace std;

const int MAX = 100010;
const int MAXN = 1010;

struct Node{
int x,y,z;
}p[MAX];

vector<int>G[MAX];

int mark[MAXN][MAXN];
int d[MAX],n,m,q;

void init(){
d[0] = 0;
memset(mark, 0, sizeof(mark));
for (int i = 0; i<MAX; i++) G[i].clear();
}

void dfs(int u){
for (int i = 0; i<G[u].size(); i++){
int v = G[u][i];
int s = p[v].y, t = p[v].z;

if (p[v].x == 1){
if (mark[s][t] == 1){
d[v] = d[u];
dfs(v);
}
else{
mark[s][t] = 1;
d[v] = d[u] + 1;
dfs(v);
mark[s][t] = 0;
}
}

if (p[v].x == 2){
if (mark[s][t] == 0){
d[v] = d[u];
dfs(v);
}
else{
mark[s][t] = 0;
d[v] = d[u] - 1;
dfs(v);
mark[s][t] = 1;
}
}

if (p[v].x == 3){
int cnt = 0;
for (int j = 1; j<=m; j++){
if (mark[s][j] == 1){
cnt--;
mark[s][j] = 0;
}
else{
cnt++;
mark[s][j] = 1;
}
}

d[v] = d[u] + cnt;
dfs(v);
for (int j = 1; j<=m; j++){
if (mark[s][j] == 0){
mark[s][j] = 1;
}
else{
mark[s][j] = 0;
}
}
}

if (p[v].x == 4){
d[v] = d[u];
dfs(v);
}
}
}

int main(){
while (scanf("%d%d%d",&n,&m, &q) != EOF){

init();

for (int i = 1; i<=q; i++){
scanf("%d", &p[i].x);
if (p[i].x==1 || p[i].x == 2){
scanf("%d%d",&p[i].y, &p[i].z);
G[i-1].push_back(i);
}
if (p[i].x == 3){
scanf("%d", &p[i].y);
G[i-1].push_back(i);
}
if (p[i].x == 4){
scanf("%d", &p[i].y);
G[p[i].y].push_back(i);
}
}

dfs(0);

for (int i = 1; i<=q; i++) printf("%d\n", d[i]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息