【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)\)。
- 如果操作后 \(top\) 为 \(0\),那么如果 \(s[r]\) 和 \(id\) 匹配,那么返回 \((l+1,l+1,0,0)\),否则返回
注意当 \(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);
}
}