zoukankan      html  css  js  c++  java
  • 联赛模拟测试20 D. Drink


    其实这道题可以当作是一道模拟???
    这道题其实本来用字符输入输出的选学卡常操作是可以过掉这道题的,但是由于划水之星skyh的存在这种做法立即就被卡成(95)了,所以还是让我们来打正解吧.
    题中的部分分应该很好拿,前(30)分直接暴力就行了,中间的(30)分我们吧所有的旋转储存下来,最后再一起旋转出来就可以了.
    然而正解和上面的没有半点联系
    我们需要介绍一种新的数据结构: 十字链表, 和普通链表不同的是我们需要同时记录上下左右四个方向,这样我们在旋转一个正方形的时候只需要处理边界的一些点就可以了,可以将单次时间复杂度降为(O(n)),可能常数比较(Huge),但是容易注意到矩形里面的点旋转之后方向就会乱掉,如果我们在处理的时候暴力判断的话会让码长到飞起,既然常数已经这么大了,我们干脆就不要注意常数了,我们可以发现矩形只会向一个方向旋转,于是我们只需要确定矩形的一个方向其他的也就知道了,于是我们每次枚举到一个矩形直接把它再暴力转回来,就可以方便许多,为方便确认一个基准的方向,我们可以在矩形的外侧再围一圈永远不会被操作的点,以此作为基准的方向,矩形里面的其他的点就可以一边走一边转回来了.这样这道问题就被我们解决了.代码不算长.时间复杂度为(Huge O)((mn)), 注意到常数有多大了吧

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    const int N = 2e3 + 10;
    struct Node {
    	int l, r, u, d, num;
    	Node() {
    		num = -1;
    	}
    } edge[N * N];
    int id[N][N];
    void zig(int now) { // 左旋
    	int cnt = edge[now].u;
    	edge[now].u = edge[now].l;
    	edge[now].l = edge[now].d;
    	edge[now].d = edge[now].r;
    	edge[now].r = cnt;
    }
    void zag(int now) { // 右旋
    	int cnt = edge[now].u;
    	edge[now].u = edge[now].r;
    	edge[now].r = edge[now].d;
    	edge[now].d = edge[now].l;
    	edge[now].l = cnt;
    }
    void swp_down(int from) { //将当前点下面的点转回来
    	int to = edge[from].d;
    	if(edge[to].u == from) return;
    	else if(edge[to].l == from) zig(to);
    	else if(edge[to].r == from) zag(to);
    	else if(edge[to].d == from) {
    		zig(to); zig(to);
    	}
    }
    void swp_right(int from) { //将当前点右面的点转回来
    	int to = edge[from].r;
    	if(edge[to].l == from) return;
    	else if(edge[to].d == from) zig(to);
    	else if(edge[to].u == from) zag(to);
    	else if(edge[to].r == from) {
    		zig(to); zig(to);
    	}
    }
    void swp_left(int from) { //将当前点左面的点转回来
    	int to = edge[from].l;
    	if(edge[to].r == from) return;
    	else if(edge[to].u == from) zig(to);
    	else if(edge[to].d == from) zag(to);
    	else if(edge[to].l == from) {
    		zig(to); zig(to);
    	}
    }
    void swp_up(int from) { //将当前点上面的点转回来
    	int to = edge[from].u;
    	if(edge[to].d == from) return;
    	else if(edge[to].r == from) zig(to);
    	else if(edge[to].l == from) zag(to);
    	else if(edge[to].u == from) {
    		zig(to); zig(to);
    	}
    }
    int find(int x, int y) {//从起点查找到当前需要操作的点的编号
    	int now = id[x][0];
    	while(y--) {
    		swp_right(now);
    		now = edge[now].r;
    	} 
    	return now;
    }
    std::vector<int> L, R, U, D;
    void Connect(int l, int d, int r, int u) { //上下左右对应的四个位置分别断边和连接新边
    	int L = edge[l].l, D = edge[d].d;
    	int R = edge[r].r, U = edge[u].u;
    	edge[l].l = U;
    	edge[u].u = R;
    	edge[r].r = D;
    	edge[d].d = L;
    	edge[L].r = d;
    	edge[U].d = l;
    	edge[R].l = u;
    	edge[D].u = r;
    }
    void Rotate(int x, int y, int d) { //旋转选定矩形
    	int now = find(x, y);
    	L.clear();
    	R.clear();
    	U.clear();
    	D.clear();
    	L.push_back(now);
    	for(int i = 1; i < d; ++i) { //围绕着操作矩形把四周的的点村下来
    		swp_left(now);
    		swp_down(now);
    		now = edge[now].d;
    		L.push_back(now);
    	}
    	swp_left(now);
    	swp_down(now);
    	D.push_back(now);
    	for(int i = 1; i < d; ++i) {
    		swp_down(now);
    		swp_right(now);
    		now = edge[now].r;
    		D.push_back(now);
    	}
    	swp_down(now);
    	swp_right(now);
    	R.push_back(now);
    	for(int i = 1; i < d; ++i) {
    		swp_up(now);
    		swp_right(now);
    		now = edge[now].u;
    		R.push_back(now);
    	}
    	swp_up(now);
    	swp_right(now);
    	U.push_back(now);
    	for(int i = 1; i < d; ++i) {
    		swp_up(now);
    		swp_left(now);
    		now = edge[now].l;
    		U.push_back(now);
    	}
    	swp_up(now);
    	swp_left(now);
    	for(int i = 0; i < L.size(); ++i) {
    		Connect(L[i], D[i], R[i], U[i]);
    	}
    }
    int tot;
    int main() {
    	//freopen("a.in", "r", stdin);
    	freopen("drink.in", "r", stdin);
    	freopen("drink.out", "w", stdout);
    	int n, m, q;
    	scanf("%d%d%d", &n, &m, &q);
    	for(int i = 0; i <= m + 1; ++i) { //存一圈无用的节点
    		id[0][i] = ++tot;
    	}
    	for(int i = 1; i <= n; ++i) {
    		id[i][0] = ++tot;
    		id[i][m + 1] = ++tot;
    	}
    	for(int i = 0; i <= m + 1; ++i) {
    		id[n + 1][i] = ++tot;
    	}
    	for(int i = 1; i <= n; ++i) {
    		for(int j = 1; j <= m; ++j) {
    			int x;
    			scanf("%d", &x);
    			edge[++tot].num = x;
    			id[i][j] = tot;
    		}
    	}
    	for(int i = 0; i <= n + 1; ++i) {
    		for(int j = 0; j <= m + 1; ++j) { //连边
    			if(i != 0) edge[id[i][j]].u = id[i - 1][j];
    			if(i != n + 1) edge[id[i][j]].d = id[i + 1][j];
    			if(j != 0) edge[id[i][j]].l = id[i][j - 1];
    			if(j != m + 1) edge[id[i][j]].r = id[i][j + 1];
    		}
    	}
    	while(q--) {
    		int x, y, d;
    		scanf("%d%d%d", &x, &y, &d);
    		Rotate(x, y, d);
    	}
    	int now = 1;
    	for(int i = 1; i <= n; ++i) {//输出答案
    		swp_down(now);
    		now = edge[now].d;
    		swp_right(now);
    		for(int j = edge[now].r; ~edge[j].num; j = edge[j].r) {
    			printf("%d ", edge[j].num);
    			swp_right(j);
    		}
    		puts("");
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    0302思考并回答一些问题
    0104 自下而上
    1203 有穷自动机的构造
    11 10我的评论
    1029 C语言文法翻译(2)
    1014 对编译程序的心得与总结
    我们小组的编译程序
    0921 词法分析程序
    0909 关于编译原理的思考
    数据库设计之数据库设计三大范式
  • 原文地址:https://www.cnblogs.com/li-jia-hao/p/13886382.html
Copyright © 2011-2022 走看看