zoukankan      html  css  js  c++  java
  • CF 707D Persistent Bookcase

    CF 707D Persistent Bookcase

    题目链接:洛谷 CF 707D Persistent Bookcase CF 707D Persistent Bookcase

    算法标签: 操作树深搜

    题目描述:

    维护一个二维零一矩阵((n,m le 1000)),支持四种操作(不超过 (10^5) 次):

    • ((i,j)) 置一

    • ((i,j)) 置零

    • 将第 (i) 行零一反转

    • 回到第 (K) 次操作前的状态

      每次操作后输出全局一共有多少个一

    题解:

    操作树(其实写完我也不知道这是啥)

    首先我们看看这四个操作:

    • A 修改
    • B 修改
    • C 修改
    • D 恢复

    所以是不是可以看出来这道题毒瘤的是啥了嘛???? —— D操作啊!!!!!

    这时候就需要操作树,大致就是按照操作来操作出一棵操作树,之后在操作树上操作各种操作,之后记录每次操作操作后的答案。 [滑稽.jpg] 【其实这么说我也看不懂】

    额……额……额……额……额…… 好了好了说正事。

    对于前三种情况,由于我们只是在这次进行修改,显然当前操作之后就是下一个操作,所以我们就在当前操作下一操作之间连一条边。

    对于第四种情况,我们需要得到第(k)次操作的状态,所以我们就需要在第(k)次操作时候直接得出当前操作的答案,也就是需要在当前操作(k)次操作之间连一条边,这样构成了一棵操作树(其实根本不是像上边那么晕)。

    之后我们DFS这棵树,对每一个点进行该点的操作,之后直接走他的边,得出它的子节点的答案,回溯。最终我们可以得到所有点的答案。

    由于时空问题(踏马乐 TMLE)(bitset)这个好用且备受喜爱的东西来存状态。

    P.S.如果还是不懂看代码注释鸭!!!

    AC代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define setI(x) freopen(x".in", "r", stdin); 
    #define setO(x) freopen(x".out", "w", stdout); 
    #define setIO(x) setI(x) setO(x)
    
    typedef long long ll;
    
    //quick_read
    
    char *p1, *p2, buf[100000];
    
    #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ ) 
    
    int rd() {
    	int x = 0, f = 1;
    	char c = nc();
    	while (c < 48) {
    		if (c == '-') {
    			f = -1;
    		}
    		c = nc();
    	}
    	while (c > 47) { 
    		x = (((x << 2) + x) << 1) + (c ^ 48);
    		c = nc();
    	}
    	return x * f;
    }
    
    // quick_read over
    
    const int N = 1010;
    
    const int M = 1e5 + 10;
    
    int n, m, q, tot;
    
    int ans[M], opt[M], x[M], y[M];
    
    bitset <N> all, mp[N];
    
    // all 表示全1状态,用于取反
    // mp[N]  存放状态
    
    vector <int> edge[M];
    
    // 存放树边
    
    void dfs(int pos) {
    	bool flag = 0;  // 这个点需不需要回溯
    	if (opt[pos] == 1 && mp[x[pos]][y[pos]] == 0) { // 操作A
    		flag = 1;
    		tot ++ ;
    		mp[x[pos]][y[pos]] = 1;
    	}
    	else if (opt[pos] == 2 && mp[x[pos]][y[pos]] == 1) {  // 操作B
    		flag = 1;
    		tot -- ;
    		mp[x[pos]][y[pos]] = 0;
    	}
    	else if (opt[pos] == 3) {  // 操作C
    		flag = 1;
    		tot -= mp[x[pos]].count();  // 减去当前1的个数
    		mp[x[pos]] ^= all;  // 取反
    		tot += mp[x[pos]].count();  // 加上取反后1的个数
    	}
    	ans[pos] = tot;  // 记录答案
    	for (int i = 0; i < (int)edge[pos].size(); i ++ ) {
    		dfs(edge[pos][i]);  // 爬子节点
    	}
    	if (flag) {  // 回溯
    		if (opt[pos] == 1) {
    			tot -- ;
    			mp[x[pos]][y[pos]] = 0;
    		}
    		else if (opt[pos] == 2) {
    			tot ++ ;
    			mp[x[pos]][y[pos]] = 1;
    		}
    		else if (opt[pos] == 3) {
    			tot -= mp[x[pos]].count();
    			mp[x[pos]] ^= all;
    			tot += mp[x[pos]].count();
    		}
    	}
    }
    
    int main() {
    	// setIO("now");
    
    	n = rd(), m = rd(), q = rd();
    	for (int i = 1; i <= m; i ++ ) {
    		all.set(i);
    		// 现学bitset,表示把i+1位置成1
    	}
    	memset(ans, -1, sizeof ans);
    	for (int i = 1; i <= q; i ++ ) {
    		opt[i] = rd(); // 操作种类
    		x[i] = rd();  // 操作第一关键字
    		if (opt[i] == 4) {
    			edge[x[i]].push_back(i);  // 恢复 建边
    		}
    		else {
    			if (opt[i] < 3) {
    				y[i] = rd(); // 操作第二关键字
    			} 
    		}
    		if (opt[i] < 4) {
    			edge[i - 1].push_back(i);  // 修改 建边
    		}
    	}
    	dfs(0);  // 爬树
    	for (int i = 1; i <= q; i ++ ) {
    		printf("%d
    ", ans[i]);
    	}
    	return 0;
    
    }
    
  • 相关阅读:
    STP-5-STP配置及分析
    PostgreSQL-10-数据运算与函数
    PostgreSQL-9-别名与动态表复制
    PostgreSQL-8-数据合并
    PostgreSQL-7-数据连接
    PostgreSQL-6-数据分组
    STP-4-每VLAN生成树和Trunk上的STP
    VLAN-6-VLAN Trunk协议(VTP)
    STP-3-收敛到新的STP拓扑
    STP-2-三个选择
  • 原文地址:https://www.cnblogs.com/littleseven777/p/11845499.html
Copyright © 2011-2022 走看看