【LOJ#3005】Limited Memory

题目

题目链接:https://loj.ac/p/3005
由于题目太长了还不能复制,这里就不放题面了(

思路

\(n\leq 100\),询问次数是 \(15000\),那么可以枚举每一个位置的字符,每次暴力 \(O(n)\) 找它匹配的字符。
只能记录一个 \(22\) 位的二进制数。那么肯定是要把若干信息状压后扔进去。
我们记目前寻找匹配的位置 \(l\),以及现在枚举到的位置 \(r\),括号匹配肯定需要用一个栈来维护,但是因为只需要匹配位置 \(l\) 的字符,其他字符是否匹配都无关,所以我们也只需要记录栈的大小 \(top\)。最后再记录位置 \(l\) 的字符是 <>[] 的哪一种。
\(l,r,top\) 最大都是 \(100\),在二进制下最多是 \(7\) 位。再加上我们记录了第 \(l\) 个字符,恰好需要用到 \(22\) 位。
由于目前需要寻找匹配的字符 \(l\) 有可能是左括号或者右括号,因为匹配的本质是一样的,所以我们只考虑 \(l\) 是左括号(<[) 的情况。
不断重复以下过程:

  • 如果 \(l>n\),返回 -1
  • 如果 \(r>n\),返回 -2
  • 如果 \(l=r\),询问 Get(l),返回 \((l,r+1,1,id)\),其中 \(id\) 如果 \(s[l]\)< 则为 \(0\),否则为 \(1\)
  • 如果 \(l<r\),询问 Get(r),如果字符是 <[ 则让 \(top\) 加一,否则让 \(top\) 减一。
    • 如果操作后 \(top\)\(0\),那么如果 \(s[r]\)\(id\) 匹配,那么返回 \((l+1,l+1,0,0)\),否则返回 -2
    • 如果操作后 \(top\) 不为 \(0\),那么返回 \((l,r+1,top,id)\)

注意当 \(s[l]\)>] 的时候需要完全反过来搞。
时间复杂度 \(O(n^2)\),操作次数显然是严格小于 \(n^2\) 的。

代码

#include <bits/stdc++.h>
#include "memory.h"
using namespace std;

int calc(int l,int r,int top,int id)
{
	return (l<<15)+(r<<8)+(top<<1)+id;
}

int Memory(int n,int m)
{
	// LLLLLLLRRRRRRRTTTTTTTC
	if (!m) return calc(1,1,0,0);
	int l=(m>>15),r=(m>>8)&127,top=(m>>1)&127;
	if (l>n) return -1;
	if (!r || r>n) return -2;
	char ch=Get(r);
	if (l==r)
	{
		if (ch=='[' || ch=='<') return calc(l,r+1,1,ch=='[');
		if (ch==']' || ch=='>') return calc(l,r-1,1,ch==']');
	}
	if (l<r)
	{
		if (ch=='<' || ch=='[') top++;
		if (ch=='>' || ch==']') top--;
		if (!top)
		{
			if ((m&1) && ch=='>') return -2;
			if (!(m&1) && ch==']') return -2;
			return calc(l+1,l+1,0,0);
		}
		return calc(l,r+1,top,m&1);
	}
	if (l>r)
	{
		if (ch=='>' || ch==']') top++;
		if (ch=='<' || ch=='[') top--;
		if (!top)
		{
			if ((m&1) && ch=='<') return -2;
			if (!(m&1) && ch=='[') return -2;
			return calc(l+1,l+1,0,0);
		}
		return calc(l,r-1,top,m&1);
	}
}
posted @ 2021-07-21 15:59  stoorz  阅读(59)  评论(0编辑  收藏  举报