卡 常 战 神——P10436 [JOIST 2024] 卡牌收集 / Card Collection 题解

前言

这篇题解讲的是 DFA + 等价类分治做法,卡了两天的常但是加深了对很多东西的理解,可以在 LOJ 和洛谷上通过。长文警告,极度卡常,慎重尝试

在更慢的 OJ 上需要使用指令集。

洛谷记录 LOJ记录

Part 1 提出算法

我们先来做一遍这个题。

先考虑一下单询问,发现我们并不关心每个卡牌的数值具体是多少,而只关心卡牌数值相对于目标的大小关系。如果卡牌数值比目标小就是 -1,相等是 0,大于是 +1,那么我们就可以把一张卡牌变成一个二元组 (-1/0/+1,-1/0/+1)。我们的目标就是在这种情况下去判定能否合成 (0,0)。

这类相邻合成问题是 DFA 的专长。我们将二元组看做 0~8 的字符集,然后定义出 9*9 的合并规则后,我们就可以通过暴力方式跑出能够识别合法串的 DFA。

具体地,我们根据 Myhill-Nerode 定理,只需要枚举一个小字符串集合 X,再枚举一个小字符串集合 Z,对于 X 中的任意两个串 \(X_1,X_2\),我们判定二者属于同一等价类(或者说自动机状态)的充要条件是,对于任意的 Z,\(X_1+Z\) 的接受性(或者说合法性)和 \(X_2+Z\) 的接受性都一致。

这里 X,Z 集合枚举的规模都是靠经验来定,只要你试了几个数发现增大 X/Z 并没有使自动机大小/转移边变化的时候就差不多建对了。

建自动机的代码可以参考另一篇题解。这里只贴出来建完的自动机。

const int idx=24;
const bool acc[idx+5]={0 ,0,0,0,0,0,1,0,0,0,0 ,1,0,1,0,0 ,0,0,1,0,0 ,0,0,0,0};
const int to[idx+5][9]={
    {},
    {2,3,4,5,6,7,8,9,2},//1
    {2,3,10,5,6,7,12,9,2},//2
    {3,3,3,18,18,14,15,15,3},//3-
    {10,3,4,7,20,7,10,3,10},//4
    {5,13,16,5,13,16,5,19,5},//5-
    {11,11,17,11,11,11,22,11,11},//6-
    {7,14,7,16,18,7,16,18,7},//7-
    {12,9,12,5,21,5,8,9,12},//8
    {9,15,15,19,13,13,9,9,9},//9-
    {10,3,10,16,6,7,23,15,10},//10
    {11,11,11,11,11,11,11,11,11},//11-
    {12,15,23,5,6,16,12,9,12},//12
    {11,11,11,11,11,11,22,11,11},//13-
    {14,14,14,18,18,14,18,18,14},//14
    {15,15,15,11,11,11,15,15,15},//15-
    {16,11,16,16,11,16,16,11,16},//16-
    {11,11,17,11,11,11,11,11,11},//17-
    {11,11,17,11,11,11,11,11,11},//18-
    {19,13,13,19,13,13,19,19,19},//19-
    {20,14,20,18,18,14,24,18,20},//20
    {21,13,24,19,13,13,21,19,21},//21
    {11,11,11,11,11,11,22,11,11},//22-
    {23,15,23,16,6,16,23,15,23},//23
    {24,11,24,11,11,11,24,11,24}//24-
};

让我们先暂时把这个 DFA 当成黑盒,不去关心其内部转移的形式。

至此已经获得了 \(O(nq)\) 的暴力解法,将每个询问按顺序放进自动机里跑就行了。

Part 2 复杂度优化

以下设 \(|S|=24\) 为自动机规模。

我们发现这样一个事实:在长为 \(B\) 的区间中,本质不同的询问只有 \(O(B^2)\) 种。由于大小关系只有 <,=,> 三种,所以具体的数字是 \((2B+1)^2\) 种。

这告诉我们什么呢?我们如果以块长 \(B\) 为阈值分块,那么块内可以 \(O(B^2|S|)\) 预处理所有本质不同类的结果(一个类求出如果进去前在某状态,走了之后会到哪个状态),然后所有询问就只用 \(O(1)\) 推进一个块。

如何预处理呢?我们在同块内序列分治,先处理左半边的映射,再处理右半边,再进行合并。容易得到 \(T(n)=2T(\frac{n}{2})+O(n^2|S|)\),主定理分析这个就是 \(O(n^2|S|)\) 的。

由于 \(n,q\) 同级,分析一下很容易得到 \(O(n\sqrt{n|S|})\) 的平衡结果。计算一下是有通过的可能的。事实上,这就是大名鼎鼎的等价类分治的一种实现方式。

但是如果你直接写,很快会被打醒。

首先一个最大的问题是,在这种情况下你根本没法把 \(B\) 设的太大。不论空间开不开的下,即使你逐块处理,预处理的常数也特别特别大。经过试验,\(B=64\) 时预处理在洛谷上就要将近 3s,\(B=100\) 就已经无法在 4s 内跑完了。我们询问的复杂度是 \(O(\frac{qn}{B})\),这样的情况显然无法满足我们的需求,实际上也是。

说不定你可以把 20s 以上的东西优化直接冲过去,但我选择在算法层面进行卡常。

Part 3 卡常第一阶段:分治内卡常

我们抛弃阈值的想法,直接进行序列分治。

我们维护一个全局数组 \(now\) 表示某个询问当前所在的自动机状态。分治函数 solve(vector<> a,vector<> q) 执行后需要保证所有 \(q\) 内存储的询问都被正确地按顺序推进了 \(a\) 中的所有卡牌。

为了保证复杂度,我们需要在每一层都进行等价类的去重。具体地,我们在分治函数中,对于两维中的某一维,将 \(a\)\(q\) 进行排序双指针式的离散化。离散化后,如果某些询问的 \((x,y,now_{id})\) 三元组(前两维数值,第三维表示当前在自动机上的状态)完全一致,我们就可以将其压成同一个询问往下递归。等递归回来再展开成原来的询问。

void solve(vector<arr> a,vector<arrr> q){
	int n=a.size(),m=q.size();
	if(n<=1){
		arr v=a[0];
		for(auto x:q){
			int sx=(v[0]<x[0]?0:(v[0]==x[0]?1:2));
			int sy=(v[1]<x[1]?0:(v[1]==x[1]?1:2));
			now[x[2]]=to[now[x[2]]][sx*3+sy];
		}return ;
	}
	vector<arr> prea(a);
	for(int c:{0,1}){
		int mxv=0;
		for(auto x:a) mxv=max(mxv,x[c]);
		for(auto x:q) mxv=max(mxv,x[c]);
		vector<int> h(mxv+1,0);
		
		int tot=-1,lst=-1;
		srt(a,[&](const arr &x){return x[c];});
		srt(q,[&](const arrr &x){return x[c];});
		int j=0;
		for(int i=0;i<m;i++){
			while(j<n&&a[j][c]<q[i][c]){
				++tot;
				h[a[j][c]]=tot;lst=-1;j++;//??!!
			}
			if(lst==-1) ++tot;
			h[q[i][c]]=tot;lst=q[i][c];
		}while(j<n){
			++tot;
			h[a[j][c]]=tot;lst=-1;j++;
		}
		for(auto &x:prea) x[c]=h[x[c]];
		for(auto &x:q) x[c]=h[x[c]];
	}
	vector<int> pre(q.size(),0);
	vector<arrr> nxtq;
	srt(q,[&](const arrr &x){return now[x[2]];});
	for(int i=0,j;i<m;i=j+1){
		j=i;while(j<m&&q[i][0]==q[j][0]&&q[i][1]==q[j][1]
			&&now[q[i][2]]==now[q[j][2]]) j++;j--;
		for(int k=i;k<=j;k++) pre[k]=i;
		nxtq.push_back(q[i]);
	}
	int mid=(n-1)>>1;
	vector<arr> la,ra;
	for(int i=0;i<=mid;i++) la.push_back(prea[i]);
	for(int i=mid+1;i<n;i++) ra.push_back(prea[i]);
	solve(la,nxtq);solve(ra,nxtq);
	for(int i=0;i<m;i++) now[q[i][2]]=now[q[pre[i]][2]];
}

上面的代码没有经过卡常,相对易读,没有看懂文字的可以看代码或者 AI 辅助理解。

这样我们每一个节点的复杂度是 \(O(\min(q,n^2|S|))\),仍然可以分析成 \(O(n\sqrt{n|S|})\),但是卡的并不满。此时实测时长 30s 左右。

优化 1:减少排序

我们发现我们对于整个 \(q\) 排序很不值得,我们只是想要 \(q\) 里的值和 \(a\) 里的值有序。因此,我们专门开一个有序的数组 qv[2] 来记录 \(q\) 中两维的数值,由于离散化并不改变其有序性,我们就可以省掉 \(q\) 的排序。

优化 2:去重优化

我们去重时依赖于将 \(q\) 的三维进行排序,但是现在要优化排序。我们考虑将 \(q\) 始终按照第一维排序,注意到后两维的上界分别是 \((2n+1,|S|)\),我们直接以 \(|S|\times y+now\) 作为其哈希就可以直接用一个数组存下。

然后将一些 vector 优化成静态 static 数组即可。

void solve(vector<arr> a,vector<arrr> q,array<vector<int>,2> qv){
	int n=a.size(),m=q.size(),l[2]={(int)qv[0].size(),(int)qv[1].size()};
	if(n<=1){
		arr v=a[0];
		for(auto x:q){
			int sx=(v[0]<x[0]?0:(v[0]==x[0]?1:2));
			int sy=(v[1]<x[1]?0:(v[1]==x[1]?1:2));
			now[x[2]]=to[now[x[2]]][sx*3+sy];
		}return ;
	}
	vector<arr> prea(a);
	for(int c:{0,1}){
		static int h[2*maxn];
		
		int tot=-1,lst=-1;
		srt(a,[&](const arr &x){return x[c];});
		//qv has been sorted.
		int j=0;
		for(int i=0;i<l[c];i++){
			while(j<n&&a[j][c]<qv[c][i]){
				++tot;
				h[a[j][c]]=tot;lst=-1;j++;//??!!
			}
			if(lst==-1) ++tot;
			h[qv[c][i]]=tot;lst=qv[c][i];
		}while(j<n){
			++tot;
			h[a[j][c]]=tot;lst=-1;j++;
		}
		for(auto &x:prea) x[c]=h[x[c]];
		for(auto &x:q) x[c]=h[x[c]];
		for(auto &x:qv[c]) x=h[x];
		l[c]=unique(qv[c].begin(),qv[c].end())-qv[c].begin();
		qv[c].resize(l[c]);
	}
	vector<int> pre(q.size(),0);
	vector<arrr> nxtq;
	static int hs[maxn*2*idx+idx+5];
	for(int i=0,j;i<m;i=j+1){
		j=i;while(j<m&&q[i][0]==q[j][0]) j++;j--;
		for(int k=i;k<=j;k++){
			int d=id(q[k]);
			if(hs[d]==0){
				nxtq.push_back(q[k]);
				hs[d]=k;
			}pre[k]=hs[d];
		}for(int k=i;k<=j;k++) hs[id(q[k])]=0;
	}
	int mid=(n-1)>>1;
	vector<arr> la,ra;
	for(int i=0;i<=mid;i++) la.push_back(prea[i]);
	for(int i=mid+1;i<n;i++) ra.push_back(prea[i]);
	solve(la,nxtq,qv);solve(ra,nxtq,qv);
	for(int i=0;i<m;i++) now[q[i][2]]=now[q[pre[i]][2]];
}

现在实测 13s 左右,递归 \(q\) 的总量 \(5\times 10^8\) 左右。

Part 4 卡常第二阶段:自动机卡常

刚才已经是我们在其他部分能卡到的极限了。现在我们必须拆开自动机的黑盒,去转移里面剪枝。

{2,3,4,5,6,7,8,9,2},//1
{2,3,10,5,6,7,12,9,2},//2
{3,3,3,18,18,14,15,15,3},//3-
{10,3,4,7,20,7,10,3,10},//4
{5,13,16,5,13,16,5,19,5},//5-
{11,11,17,11,11,11,22,11,11},//6-
{7,14,7,16,18,7,16,18,7},//7-
{12,9,12,5,21,5,8,9,12},//8
{9,15,15,19,13,13,9,9,9},//9-
{10,3,10,16,6,7,23,15,10},//10
{11,11,11,11,11,11,11,11,11},//11-
{12,15,23,5,6,16,12,9,12},//12
{11,11,11,11,11,11,22,11,11},//13-
{14,14,14,18,18,14,18,18,14},//14
{15,15,15,11,11,11,15,15,15},//15-
{16,11,16,16,11,16,16,11,16},//16-
{11,11,17,11,11,11,11,11,11},//17-
{11,11,17,11,11,11,11,11,11},//18-
{19,13,13,19,13,13,19,19,19},//19-
{20,14,20,18,18,14,24,18,20},//20
{21,13,24,19,13,13,21,19,21},//21
{11,11,11,11,11,11,22,11,11},//22-
{23,15,23,16,6,16,23,15,23},//23
{24,11,24,11,11,11,24,11,24}//24-

首先,对于 11 号点,我们发现其只有到 11 的自环转移,因此只要当前状态是 11 就没有转移的必要了,可以不加进 nxtq 中递归。

这样 sumq 就到了 4.4e8 的量级。

再继续观察,我们发现对于 15 号点,只要这段区间中有某个卡牌的 y 和当前询问相等,就一定会到 11 号点,否则一定留在 15 号点。这样你只要记下区间中 "是否存在某个卡牌的值等于 y" 就可以直接算出来并不将它扔进 nxtq 中。

同理,16 号点也能应用此优化。

加入后 sumq 就只有 2e8 左右了。再加入一些把 vector 改成静态数组,别的自动机人类智慧,或者阈值分块就可以通过了!

最后代码很长。时间复杂度 \(O(n\sqrt{n|S|})\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
const int idx=24;
const bool acc[idx+5]={0 ,0,0,0,0,0,1,0,0,0,0 ,1,0,1,0,0 ,0,0,1,0,0 ,0,0,0,0};
const int to[idx+5][9]={
    {},
    {2,3,4,5,6,7,8,9,2},//1
    {2,3,10,5,6,7,12,9,2},//2
    {3,3,3,18,18,14,15,15,3},//3-
    {10,3,4,7,20,7,10,3,10},//4
    {5,13,16,5,13,16,5,19,5},//5-
    {11,11,17,11,11,11,22,11,11},//6-
    {7,14,7,16,18,7,16,18,7},//7-
    {12,9,12,5,21,5,8,9,12},//8
    {9,15,15,19,13,13,9,9,9},//9-
    {10,3,10,16,6,7,23,15,10},//10
    {11,11,11,11,11,11,11,11,11},//11-
    {12,15,23,5,6,16,12,9,12},//12
    {11,11,11,11,11,11,22,11,11},//13-
    {14,14,14,18,18,14,18,18,14},//14
    {15,15,15,11,11,11,15,15,15},//15-
    {16,11,16,16,11,16,16,11,16},//16-
    {11,11,17,11,11,11,11,11,11},//17-
    {11,11,17,11,11,11,11,11,11},//18-
    {19,13,13,19,13,13,19,19,19},//19-
    {20,14,20,18,18,14,24,18,20},//20
    {21,13,24,19,13,13,21,19,21},//21
    {11,11,11,11,11,11,22,11,11},//22-
    {23,15,23,16,6,16,23,15,23},//23
    {24,11,24,11,11,11,24,11,24}//24-
};
typedef array<int,2> arr;
typedef array<int,3> arrr;
template<typename T,typename Func>
void srt(vector<T> &a,Func id){
	int n=a.size(),m=0;for(auto x:a) m=max(m,id(x));
	static int cnt[2*maxn];
	static T tmp[maxn];
	for(int i=0;i<=m;i++) cnt[i]=0;
	for(auto x:a) cnt[id(x)]++;
	for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
	for(int i=n-1;i>=0;i--) tmp[--cnt[id(a[i])]]=a[i];
	for(int i=0;i<n;i++) a[i]=tmp[i];
}
//a[i][0/1]<=2*a.size()+1.
int now[maxn];
const int Bsmall=1;
const int maxdep=20;
inline int id(arrr x){return x[1]*24+now[x[2]]-1;}
long long debug_cnt_n,debug_cnt_q,debug_cnt_qv;
long long debug_cnt_15,debug_cnt_16;
void solve(int dep,
	vector<arr> a,const vector<arrr> &qq,array<vector<int>,2> &qqv,
	int qqsz){
	int n=a.size(),m=qqsz,l[2]={(int)qqv[0].size(),(int)qqv[1].size()};
	debug_cnt_n+=n,debug_cnt_q+=m,debug_cnt_qv+=l[0]+l[1];
	if(n>10000) cerr<<"solve:"<<n<<' '<<m<<' '<<l[0]<<' '<<l[1]<<endl;
	if(n <= Bsmall){
        for(int i=0;i<n;i++){
            arr v=a[i];
            for(auto x:qq){
                int sx=(v[0]<x[0]?0:(v[0]==x[0]?1:2));
                int sy=(v[1]<x[1]?0:(v[1]==x[1]?1:2));
                now[x[2]]=to[now[x[2]]][sx*3+sy];
            }
        }
        return;
    }
    if(n>10000){
    	debug_cnt_q-=m;debug_cnt_qv-=l[0]+l[1];
    	vector<arr> la,ra;int mid=(n-1)>>1;
		for(int i=0;i<=mid;i++) la.push_back(a[i]);
		for(int i=mid+1;i<n;i++) ra.push_back(a[i]);
		solve(dep+1,la,qq,qqv,(int)qq.size());solve(dep+1,ra,qq,qqv,(int)qq.size());
		return ;
	}
	static arrr vecq[maxdep][maxn];
	auto &q=vecq[dep];
	for(int i=0;i<m;i++) q[i][2]=qq[i][2];
	array<vector<int>,2> qv=qqv;
	vector<arr> prea(a);
	int mxv[2]={-1,-1};
	static int h[2*maxn],isa[2][2*maxn];
	for(int c:{0,1}){
		
		int &tot=mxv[c];int lst=-1;
		srt(a,[&](const arr &x){return x[c];});
		//qv has been sorted.
		int j=0;
		for(int i=0;i<l[c];i++){
			while(j<n&&a[j][c]<qv[c][i]){
				++tot;
				h[a[j][c]]=tot;
				lst=-1;j++;//??!!
			}
			if(lst==-1) ++tot;
			h[qv[c][i]]=tot;lst=qv[c][i];
		}while(j<n){
			++tot;
			h[a[j][c]]=tot;
			lst=-1;j++;
		}
		for(auto &x:prea) x[c]=h[x[c]],isa[c][x[c]]=1;
		for(int k=0;k<m;k++) q[k][c]=h[qq[k][c]];
		for(auto &x:qv[c]) x=h[x];
		l[c]=unique(qv[c].begin(),qv[c].end())-qv[c].begin();
		qv[c].resize(l[c]);
	}
//	for(auto x:prea) printf("afta:%d %d\n",x[0],x[1]);
//	for(auto x:q) printf("aftq:%d %d %d\n",x[0],x[1],now[x[2]]);
	static int vecpre[maxdep][maxn];
	auto &pre=vecpre[dep];
	vector<arrr> nxtq;
	static int hs[maxn*2*idx+idx+5];
	for(int i=0,j;i<m;i=j+1){
		j=i;while(j<m&&q[i][0]==q[j][0]) j++;j--;
		static int us[maxn],uc;
		uc=0;
		for(int k=i;k<=j;k++){
			int d=id(q[k]);
			if(hs[d]==0){
				us[++uc]=d;
				bool flg=1;
				int &u=now[q[k][2]];
				//humanity intelligence.
				if(u==11) flg=0;
				else if(u==17||u==18){
					if(q[k][0]!=mxv[0]||q[k][1]!=0)
						u=11,flg=0;
				}else if(u==22||u==13){
					if(q[k][0]!=0||q[k][1]!=mxv[1])
						u=11,flg=0;
				}else if(u==15){
					if(isa[0][q[k][0]])
						u=11,flg=0;
					else
						u=15,flg=0;
					debug_cnt_15++;
				}else if(u==16){
					if(isa[1][q[k][1]])
						u=11,flg=0;
					else
						u=16,flg=0;
					debug_cnt_16++;
				}else if(u==24){
					if(isa[0][q[k][0]]||isa[1][q[k][1]])
						u=11,flg=0;
					else
						u=24,flg=0;
				}else if(u==6){
					if(isa[0][q[k][0]]||isa[1][q[k][1]])
						u=11,flg=0;
				}else if(u==3){
					if(q[k][0]==mxv[0]&&!isa[0][q[k][0]])
						u=3,flg=0;
				}else if(u==5){
					if(q[k][1]==mxv[1]&&!isa[1][q[k][1]])
						u=5,flg=0;
				}else if(u==7){
					if(q[k][1]==0&&!isa[1][q[k][1]])
						u=7,flg=0;
				}else if(u==9){
					if(q[k][0]==0&&!isa[0][q[k][0]])
						u=9,flg=0;
				}else if(u==19){
					if(q[k][0]==0&&!isa[0][q[k][0]])
						u=19,flg=0;
					else if(q[k][1]==mxv[1]&&!isa[1][q[k][1]])
						u=19,flg=0;
				}
				
				if(flg) nxtq.push_back(q[k]);
				hs[d]=k;
			}pre[k]=hs[d];
		}for(int k=1;k<=uc;k++) hs[us[k]]=0;
	}
	for(int c:{0,1}) for(auto &x:prea) isa[c][x[c]]=0;
	int mid=(n-1)>>1;
	vector<arr> la,ra;
	for(int i=0;i<=mid;i++) la.push_back(prea[i]);
	for(int i=mid+1;i<n;i++) ra.push_back(prea[i]);
	solve(dep+1,la,nxtq,qv,nxtq.size());solve(dep+1,ra,nxtq,qv,nxtq.size());
	for(int i=0;i<m;i++) now[q[i][2]]=now[q[pre[i]][2]];
}
int n,m;
arr aa[maxn],b[maxn];

int main(){
	freopen("cards.in","r",stdin);
	freopen("cards.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d%d",&aa[i][0],&aa[i][1]);
	for(int i=1;i<=m;i++) scanf("%d%d",&b[i][0],&b[i][1]);
	for(int i=1;i<=m;i++) now[i]=1;
	
	vector<arr> a,prea;
	vector<arrr> q;
	for(int i=1;i<=n;i++) a.push_back(aa[i]);
	prea=a;
	for(int i=1;i<=m;i++) q.push_back({b[i][0],b[i][1],i});
	array<vector<int>,2> qv;
	sort(q.begin(),q.end(),[&](arrr x,arrr y){return x[0]<y[0];});
	for(int c:{0,1}){
		for(auto &x:q) qv[c].push_back(x[c]);
		sort(qv[c].begin(),qv[c].end());
	}
	int l[2]={(int)qv[0].size(),(int)qv[1].size()};
	//cerr<<"ok1"<<endl;
	for(int c:{0,1}){
		unordered_map<int,int> h;
		
		int tot=-1,lst=-1;
		sort(a.begin(),a.end(),[&](arr x,arr y){return x[c]<y[c];});
		//qv has been sorted.
		int j=0;
		for(int i=0;i<l[c];i++){
			while(j<n&&a[j][c]<qv[c][i]){
				++tot;
				h[a[j][c]]=tot;lst=-1;j++;//??!!
			}
			if(lst==-1) ++tot;
			h[qv[c][i]]=tot;lst=qv[c][i];
		}while(j<n){
			++tot;
			h[a[j][c]]=tot;lst=-1;j++;
		}
		for(auto &x:prea) x[c]=h[x[c]];
		for(auto &x:q) x[c]=h[x[c]];//!!!
		for(auto &x:qv[c]) x=h[x];
		l[c]=unique(qv[c].begin(),qv[c].end())-qv[c].begin();
		qv[c].resize(l[c]);
	}//cerr<<"ok2"<<endl;
	vector<int> pre(q.size(),0);
	vector<arrr> nxtq;
	static int hs[maxn*2*idx+idx+5];
	for(int i=0,j;i<m;i=j+1){
		j=i;while(j<m&&q[i][0]==q[j][0]) j++;j--;
		for(int k=i;k<=j;k++){
			int d=id(q[k]);
			if(hs[d]==0){
				nxtq.push_back(q[k]);
				hs[d]=k;
			}pre[k]=hs[d];
		}for(int k=i;k<=j;k++) hs[id(q[k])]=0;
	}
	solve(1,prea,nxtq,qv,nxtq.size());
	for(int i=0;i<m;i++) now[q[i][2]]=now[q[pre[i]][2]];
	
	bool first=true;
    for(int i=1;i<=m;i++){
        if(acc[now[i]]){
            if(!first) printf(" ");
            first=false;
            printf("%d", i);
        }
    }
    printf("\n");
	cerr<<debug_cnt_n<<' '<<debug_cnt_q<<' '<<debug_cnt_qv<<endl;
	cerr<<debug_cnt_15<<' '<<debug_cnt_16<<endl;
	return 0;
}

posted @ 2026-01-11 14:23  runzelai  阅读(3)  评论(0)    收藏  举报