1 条题解

  • 0
    @ 2025-8-24 22:16:12

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Felix72
    公式和原理永远无法推论人情。

    搬运于2025-08-24 22:16:12,当前版本为作者最后更新于2025-04-09 09:24:12,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    桑格莉娅不止一次地启发我做网格行走相关题目:

    贴模型走路是一个很实用的技巧,可以在这类网格路径题中有效地去除一些重复冗余状态。

    比如这道题,定义首末两行、两列为边缘部分,其他为非边缘部分,我们可以发现无论怎么在非边缘部分添加障碍,都存在至少两条合法路径,如下图:

    OOOOO
    O.X.O
    OXX.O
    OX.XO
    OOOOO
    

    X 代表障碍,O 代表边缘部分,. 是普通的空地。我们发现只要存在边缘部分,我们就不用管普通的空地。

    但是边缘部分可能会改变。比如在一个空的 5×55 \times 5 网格中添加一个障碍:

    OOOX.
    O.OOO
    O...O
    O...O
    OOOOO
    

    不难发现障碍的作用是“如果处在原来的边缘部分,就把这个边缘部分挪个地方到左下角/右上角”,或者说“如果处在原来的边缘部分,则令自己左下角/右上角不可能再为边缘部分”。到底哪个角就要看自己原先处在左下角的边缘还是右上角的边缘。如果同时处在两类边缘,那说明没有路径可以走了,此时输出 TAK,我们也顺便获得了是否有路径的充要条件。

    边缘的修改是可以均摊的,因为每个点最多被一类边缘用一次,于是写一个事件驱动模拟。其中需要找最大最小值,set 常数大,换成堆就可以了。

     /* NE_Cat 4.1 */
    #include <bits/stdc++.h>
    #define lowbit(x) ((x) & (-(x)))
    using namespace std;
    
    const int N = 100010;
    typedef pair < int, int > PII;
    struct Point {int x, y, val, id;} p[N * 10];
    int n, m, k, lim[4][N]; map < PII, int > mp;
    priority_queue < PII, vector < PII >, greater < PII > > conS[4][N];
    priority_queue < PII > conL[4][N];
    
    inline bool check(int opt, Point pos)
    {
    	if(opt == 0 && pos.y <= lim[0][pos.x]) return true;
    	if(opt == 1 && pos.x >= lim[1][pos.y]) return true;
    	if(opt == 2 && pos.y >= lim[2][pos.x]) return true;
    	if(opt == 3 && pos.x <= lim[3][pos.y]) return true;
    	return false;
    }
    inline void search_A(Point pos);
    inline void search_B(Point pos);
    inline void get(int opt, int id);
    inline void get(int opt, int id)
    {
    	if(opt == 0)
    	{
    		while(!conS[0][id].empty() && conS[0][id].top().first <= lim[0][id])
    		{
    			PII cur = conS[0][id].top();
    			conS[0][id].pop();
    			search_A(p[cur.second]);
    		}
    	}
    	else if(opt == 1)
    	{
    		while(!conL[1][id].empty() && conL[1][id].top().first >= lim[1][id])
    		{
    			PII cur = conL[1][id].top();
    			conL[1][id].pop();
    			search_A(p[cur.second]);
    		}
    	}
    	else if(opt == 2)
    	{
    		while(!conL[2][id].empty() && conL[2][id].top().first >= lim[2][id])
    		{
    			PII cur = conL[2][id].top();
    			conL[2][id].pop();
    			search_B(p[cur.second]);
    		}
    	}
    	else
    	{
    		while(!conS[3][id].empty() && conS[3][id].top().first <= lim[3][id])
    		{
    			PII cur = conS[3][id].top();
    			conS[3][id].pop();
    			search_B(p[cur.second]);
    		}
    	}
    }
    inline void search_A(Point pos)
    {
    //	cerr << "A " << pos.x << " " << pos.y << '\n';
    	if(pos.x - 1 >= 1)
    	{
    		lim[0][pos.x - 1] = max(lim[0][pos.x - 1], pos.y + 1);
    		get(0, pos.x - 1);
    	}
    	if(pos.y + 1 <= m)
    	{
    		lim[1][pos.y + 1] = min(lim[1][pos.y + 1], pos.x - 1);
    		get(1, pos.y + 1);
    	}
    }
    inline void search_B(Point pos)
    {
    //	cerr << "B " << pos.x << " " << pos.y << '\n';
    	if(pos.x + 1 <= n)
    	{
    		lim[2][pos.x + 1] = min(lim[2][pos.x + 1], pos.y - 1);
    		get(2, pos.x + 1);
    	}
    	if(pos.y - 1 >= 1)
    	{
    		lim[3][pos.y - 1] = max(lim[3][pos.y - 1], pos.x + 1);
    		get(3, pos.y - 1);
    	}
    }
    
    int main()
    {
    //	freopen("text.in", "r", stdin);
    //	freopen("prog.out", "w", stdout);
    	ios::sync_with_stdio(false);
    	cin.tie(0), cout.tie(0);
    	cin >> n >> m >> k;
    	for(int i = 1; i <= k; ++i) cin >> p[i].x >> p[i].y >> p[i].val, p[i].id = i;
    	memset(lim[1], 0x3f, sizeof(lim[1]));
    	memset(lim[2], 0x3f, sizeof(lim[2]));
    	lim[0][n] = m; lim[1][1] = 1;
    	lim[2][1] = 1; lim[3][m] = n;
    	for(int i = 1, v = 0; i <= k; ++i)
    	{
    		p[i].x ^= v, p[i].y ^= v;
    		p[i].x %= n, p[i].y %= m;
    		++p[i].x, ++p[i].y;
    //		cerr << p[i].x << " " << p[i].y << " +++++++++++++++++++++++++++++\n";
    		if(mp[{p[i].x, p[i].y}]) {cout << "NIE" << '\n'; continue;}
    		mp[{p[i].x, p[i].y}] = true;
    		if(!check(0, p[i]) && !check(1, p[i]) && !check(2, p[i]) && !check(3, p[i]))
    		{
    			cout << "NIE" << '\n';
    			conS[0][p[i].x].push({p[i].y, i});
    			conL[1][p[i].y].push({p[i].x, i});
    			conL[2][p[i].x].push({p[i].y, i});
    			conS[3][p[i].y].push({p[i].x, i});
    		}
    		else if((check(0, p[i]) || check(1, p[i])) && (check(2, p[i]) || check(3, p[i])))
    		{
    			cout << "TAK" << '\n';
    			v ^= p[i].val; mp[{p[i].x, p[i].y}] = false;
    		}
    		else if(check(0, p[i]) || check(1, p[i])) cout << "NIE" << '\n', search_A(p[i]);
    		else cout << "NIE" << '\n', search_B(p[i]);
    	}
    	return 0;
    }
    /*
    
    */
    
    • 1

    信息

    ID
    4990
    时间
    3000ms
    内存
    512MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者