BZOJ1118: [POI2009]Prz

77 篇文章 0 订阅
14 篇文章 0 订阅

这题好像是个论文题??有个论文证明了这题做法的复杂度但是我太懒了没去看….

一开始我以为这题是反着构造使F(x,y)为true的x,y序列,然后得到什么结论什么的
想了一会没得到什么有用的东西就弃疗了
(连续相同的元素可以合并好像对搜索还是挺有用的一个性质
问了Claris这题居然是搜索qwq

注意到元素集合|S|<=100,因为F(x,y)的柿子每次去掉一种元素,所以不会超过100层
然后这个p(x),x的最长前缀使得W(p(x))!=W(x),可以理解为去掉了最右端的元素
s(x)同理理解为去掉了最左端的元素
然后我们yy一下这个搜索过程…
F(x,y)分到下一层,去掉最右端的元素,去掉最左端的元素
不断这样分….左右分出来的交集肯定越来越小
那么去找p(x),s(x)的时候,在用一层里,同一个位置被遍历的次数不会太多qwq(Claris说是常数次?qwq我不懂)
于是你可以把同一层的看作一个O(n)
100层就是100n了

记忆化一下,用v[dep][l1]存储搜过的状态,dep代表层数,层数,l1确定了,r1,l2,r2也是确定的

然后就可以了qwq

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 110000;
const int maxx = 110;

int n,m,al;
int a[maxn],b[maxn];
int vi[maxx],ti;
bool v[maxx][maxn];

bool solve(int d,int l1,int r1,int l2,int r2)
{
    if(v[d][l1]) return true;
    if(l1==r1) return v[d][l1]=a[l1]==b[l2]?true:false;

    int np1,np2,now; 
    now=0; ++ti;
    for(int i=l1;i<=r1;i++) 
    {
        if(vi[a[i]]!=ti) vi[a[i]]=ti,now++;
        if(now==al-d+1) { np1=i-1; break; }
    }
    now=0; ++ti;
    for(int i=l2;i<=r2;i++)
    {
        if(vi[b[i]]!=ti) vi[b[i]]=ti,now++;
        if(now==al-d+1) { np2=i-1; break; }
    }
    if(!solve(d+1,l1,np1,l2,np2)) return false;

    now=0; ++ti;
    for(int i=r1;i>=l1;i--)
    {
        if(vi[a[i]]!=ti) vi[a[i]]=ti,now++;
        if(now==al-d+1) { np1=i+1; break; }
    }
    now=0; ++ti;
    for(int i=r2;i>=l2;i--)
    {
        if(vi[b[i]]!=ti) vi[b[i]]=ti,now++;
        if(now==al-d+1) { np2=i+1; break; }
    }
    if(!solve(d+1,np1,r1,np2,r2)) return false;
    return v[d][l1]=true;
}

int main()
{
    int tcase;  read(tcase);
    while(tcase--)
    {
        ti=0; for(int i=1;i<maxx;i++) vi[i]=ti;

        read(n); read(m);
        for(int i=1;i<=n;i++) read(a[i]);
        for(int i=1;i<=m;i++) read(b[i]);

        int nn=0;
        for(int i=1;i<=n;i++) if(a[i]!=a[i-1]) 
            a[++nn]=a[i];
        n=nn;
        int mm=0;
        for(int i=1;i<=m;i++) if(b[i]!=b[i-1])
            b[++mm]=b[i];
        m=mm;

        for(int i=1;i<=n;i++) vi[a[i]]=1;
        bool flag=true; al=0;
        for(int i=1;i<=m;i++) 
        {
            if(!vi[b[i]]) { flag=false; break; }
            else if(vi[b[i]]==1) vi[b[i]]=2,al++;
        }
        for(int i=1;i<maxx&&flag;i++) if(vi[i]==1) flag=false;
        if(!flag) { puts("0");continue; }


        ti=0; for(int i=0;i<maxx;i++) vi[i]=ti;
        for(int i=1;i<maxx;i++) for(int j=1;j<=n;j++) v[i][j]=false;

        puts(solve(1,1,n,1,m)?"1":"0");
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值