10.23

(revision)货车运输

  • 最大生成树的正确性
  • 图是森林时用并查集维护两点是否相连。
  • dfs(i,i,INT_MAX)赋值为INT_MAX的妙用
  • w[u] [i]表示的时跳到f[u] [i]的沿途边权最小值。既然是求最小值,不要忘了赋初值

【线段树 带修改最大字段和】 (link)小白逛公园

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dwn(i,a,b) for(int i=(a);i>=(b);--i)
template <typename T>inline void rd(T &x){
    x=0;char c=getchar();int f=0;
    while(!isdigit(c)){f|=c=='-';c=getchar();}
    while(isdigit(c)){x=x*10+(c^48);c=getchar();}
    x=f?-x:x;
}
#define mem(a,b) memset(a,b,sizeof(a))
#define ee(i,u) for(int i=head[u];i;i=e[i].next)
/******************************************** Header Template **********************************************/
#define lson o<<1
#define rson o<<1|1
const int N=5e5+10;
int n,m;
int val[N];

struct tree{
	int l,r;
	int lmax,rmax,sum,maxx;
}t[N<<2];

inline int max_3(int a,int b,int c){
	if(a<b)a=b;
	if(a<c)a=c;
	return a;
}

inline void pushup(int o){
	t[o].sum=t[lson].sum+t[rson].sum;
	t[o].lmax=max_3(t[lson].lmax,t[lson].sum+t[rson].lmax,t[lson].sum);
	t[o].rmax=max_3(t[rson].rmax,t[rson].sum+t[lson].rmax,t[rson].sum);
	t[o].maxx=max_3(t[lson].maxx,t[rson].maxx,t[lson].rmax+t[rson].lmax);
}

inline void build(int o,int l,int r){
	t[o].l=l,t[o].r=r;
	if(l==r){
		t[o].lmax=t[o].maxx=t[o].rmax=t[o].sum=val[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(lson,l,mid);
	build(rson,mid+1,r);
	pushup(o);
}

inline void change(int o,int pos,int k){
	int l=t[o].l,r=t[o].r;
	if(l==pos && r==pos){
		t[o].sum=t[o].lmax=t[o].rmax=t[o].maxx=k;
		return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)change(lson,pos,k);
	else change(rson,pos,k);
	pushup(o);
}

tree query(int o,int x,int y){
	int l=t[o].l,r=t[o].r;
	if(l==x && y==r){
		return t[o];
	}
	int mid=(l+r)>>1;
	if(y<=mid)return query(lson,x,y);
    else if(mid<x)return query(rson,x,y);
    else{
    	tree ls=query(lson,x,mid);
    	tree rs=query(rson,mid+1,y);
        tree ans;
        ans.maxx=max_3(ls.maxx,rs.maxx,ls.rmax+rs.lmax);
        ans.lmax=max_3(ls.sum,ls.lmax,ls.sum+rs.lmax);
        ans.rmax=max_3(rs.sum,rs.rmax,rs.sum+ls.rmax);
        ans.sum=ls.sum+rs.sum;
        return ans;
    }
}

#undef int
int main(){
#define int long long
	#ifdef WIN32
	freopen("a.txt","r",stdin);
	#endif
	rd(n),rd(m);
	rep(i,1,n)rd(val[i]);
	build(1,1,n);
	while(m--){
		int op,x,y,k;
		rd(op);
		if(op==1){
			rd(x),rd(y);
			if(x>y)swap(x,y);
			tree ans=query(1,x,y);
            printf("%lld\n",ans.maxx);
		}
		else if(op==2){
			rd(x),rd(k);
			change(1,x,k);
		}
	}
    return 0;
}
/*
题目描述
给定长度为N的数列A,以及M条指令,每条指令可能是以下两种之一:
1、“1 x y”,查询区间 [x,y] 中的最大连续子段和
2、“2 x y”,把 A[x] 改成 y。
对于每个查询指令,输出一个整数表示答案。
第一行两个整数N,M。
第二行N个整数A[i]。
接下来M行每行3个整数k,x,y,k=1表示查询(此时如果x>y,请交换x,y),k=2表示修改。
对于每个查询指令输出一个整数表示答案。
N≤500000,M≤100000
N≤500000,M≤100000
5 3
1 2 -3 4 5
1 2 3
2 2 -1
1 3 2
输出样例:
2
-1
sol:
对于一个固定区间,求最大子段和我们有O(n)
的算法。但是如何维护不同区间的LIS
就成了一个问题。我们选择线段树解决区间问题,但使用线段树的话,我们需要明白,维护什么值,以
及如何进行区间操作。

那么我们思考,对于相邻的两个区间,他们的LIS
有几种可能?
1.左侧区间的LIS
2.右侧区间的LIS
3.两个区间合并后,中间新连接的部分

前两点都好理解,针对第三点我们继续思考,中间部分能够成为LIS,其实就是我们从连接部分分别向前向后,获
得一个尽可能大的前/后缀和。那么我们维护或者合并区间的LIS
就需要维护三个值,区间最大子段和,最大前缀和,最大后缀和。而我们在合并区间的时候,如何维护前/后缀和呢?
我们需要多维护一个区间和。

整理我们得到,定义区间ls,rs
合并得到区间d,每个区间维护区间和sum
,区间最大字段和maxs
,区间最大前缀和maxl
,区间最大后缀和maxr
。则合并区间时,可得关系如下
d.sum=ls.sum+rs.sum
d.maxs=max(ls.maxs,rs.maxs,ls.maxr+rs.maxl)
d.maxl=max(ls.maxl,ls.sum+rs.maxl)
d.maxr=max(rs.maxr,rs.sum+ls.maxr)
用线段树维护即可
*/

【欧拉函数】 (link)P4861按钮

题目要求\(K^{x}=1 \mod \ M\)的最小x

不难联想到欧拉定理 \(K^{phi(M)}\ =1\mod\ M\)

  • 无解的情况:__gcd(K,M)!=1
  • 引理:x一定是phi(M)的因子,那么枚举phi(M)的因子暴力判断就欧克了。

时间复杂度:\(O(\sqrt{M})\)的复杂度预处理出phi(M),再用\(O(\sqrt{phi(M)})\)的复杂度枚举因子

int K,M,ans=INT_MAX;

inline int get_phi(int x){
	int res=x;
	for(int i=2;i*i<=x;++i){
		if(x%i==0)res=res/i*(i-1);
		while(x%i==0)x/=i;
	}
	if(x>1)res=res/x*(x-1);
	return res;
}

inline int ksm(int a,int k,int mod){
	int res=1;
	for(;k;k>>=1){
		if(k&1)res=res*a%mod;
		a=a*a%mod;
	}
	return res;
}

#undef int
int main(){
#define int long long
	#ifdef WIN32
	freopen("a.txt","r",stdin);
	#endif
	rd(M),rd(K);
	if(__gcd(K,M)!=1){
		puts("Let's go Blue Jays!");
		exit(0);
	}
	int phi=get_phi(M);
	for(int i=1;i*i<=phi;i++){
		if(phi%i==0){
			if(ksm(K,i,M)==1){
				ans=min(ans,i);
			}
			else if(ksm(K,phi/i,M)==1){
				ans=min(ans,phi/i);
			}
		}
	}
	printf("%lld",ans);
    return 0;
}

【LIS 归约思想】P3902 递增

转化成已知的问题:

如果原问题是非严格单调递增,则直接求出 LIS 的⻓度,从 n 中减去就行了。

注意到 ai 都是整数,ai < \(a_{i+1}\) 相当于 ai + 1 ≤ \(a_{i+1}\)

于是

a1 < a2 < · · · < an−1 < an

就相当于

a1 + (n − 1) ≤ a2 + (n − 2) ≤ · · · ≤ \(a_n-1\)+1 ≤ an

将原数组进行预处理,就可以转化成 不降LIS 问题了。

const int N=1e5+10;
int f[N],a[N];
int len,n;

int main(){
	rd(n);
	rep(i,1,n){
		rd(a[i]);
		a[i]+=n-i;
	}
	f[++len]=a[1];
	rep(i,2,n){
		if(f[len]<a[i])f[++len]=a[i];
		else{
			int p=upper_bound(f+1,f+len+1,a[i])-f;
			f[p]=a[i];
		}
	}
	printf("%d",n-len);
    return 0;
}

1A【中序遍历 LIS 二叉树 归约思想 】P3365 改造二叉树

众所周知,二叉搜索树其实就是平衡树,平衡树的中序遍历就是一个严格LIS,遍历二叉树求出这个序列后,就转化成了上一题。

推导:

\(key[lson]<key[rt]<key[rson]\)

--->

\(key[lson]+2 \le key[rt]+1 \le key[rson]\)

那么序列的第i项需要加上的值就是n-i

那么求出更新后序列的LIS,答案就是n-len.

const int N=1e5+10;
int len,n,id;
int a[N],f[N],val[N];
int son[N][2];

inline void zhongxu(int u){
	if(son[u][0])zhongxu(son[u][0]);
	val[++id]=a[u]+(n-id);
	if(son[u][1])zhongxu(son[u][1]);
}

int main(){
	rd(n);
	rep(i,1,n)rd(a[i]);
	rep(i,2,n){
		int fa,op;
		rd(fa),rd(op);
		if(op==0)son[fa][0]=i;
		else son[fa][1]=i;
	}
	zhongxu(1);
	f[++len]=val[1];
	rep(i,2,n){
		if(f[len]<val[i])f[++len]=val[i];
		else{
			int p=upper_bound(f+1,f+len+1,val[i])-f;
			f[p]=val[i];
		}
	}
	printf("%d",n-len);
    return 0;
}

线性基

线性基是一种特殊的基,它通常会在异或运算中出现。


定义

对于自然数集 S,S 的线性基被定义为最小的集合 R,使得 S 的任何一个子集的 S′,都能找到一个 R 中的子集 R′,S′ 中所有元素的异或和等于 R′ 中元素的异或和,且对于 R 的所有子集 R′,也能找到对应的 S′。

形象地说,R 内元素随意异或得到的集合等于 S 内元素随意异或得到的集合。


性质

  1. 线性基能相互异或得到原集合的所有相互异或得到的值。
  2. 线性基是满足性质 1 的最小集合。
  3. 线性基内没有异或和为 0 的子集(因为 R 的空集的异或和为 0)。

线性组合

我们从另一个角度来考虑问题,即 x 被表示为其他数的异或和代表什么。我们把 x 拆成一个向量 x={a0,a1,…,ak−1},x=∑2i×ai,其中 k 是位数。

那么,x 能被表示成 S 的某个子集的异或和 转化为 x 能被表示成 S 中数所对应的向量在模 2 意义下的线性组合。这样一来,R 这个最的小能表示任何数的集合本质上就是 S 的最大线性无关集合

注意:这里的最小最大并不矛盾,最小意味着线性无关,最大意味着能表示任何一个数

现在,我们把数的异或,转换成了向量的线性组合


求解

高斯消元时,如果某一行被前面的行消为 0,那么代表它可以被表示成前面向量的线性组合,这也就是为什么:

方程组有解、系数矩阵行列式不为 0、系数矩阵满秩、系数矩阵的线性无关集合大小为 n ——这四个命题等价。

对于新加入的数 x,如果它能被表示成 R,就代表它能被 R 消成 0。

我们把向量反过来写,把高位系数写在左边,低位系数写在右边,对所有数做高斯消元。最终矩阵会被消成一个上三角矩阵,剩下的不为 0 的向量就是 R,而且每个向量(数)的最左边的非零数(最高位)都不相同

因为矩阵行秩等于列秩,所以 R 最多有 k(其中 k 表示位数,一般为 32)个元素。

由于是在模 2 的意义下,所以我们可以直接压位来做。记 ri 表示最高位为 i 的线性基中的数。每次新加进来一个 x 就从其最高位开始消去,如果某一位(假设为第 i 位)无法消除了就把当前的 x′ 放进 ri 中,即 x′ 在线性基 R 中。

时间复杂度:O(nlog⁡w),其中 w 为值域。

#include <cstdio>

const int N = 1e6 + 5;

int n;
unsigned int a[N], r[32];

void Gauss(int n) {
    for (int i = 1; i <= n; i++) {
        unsigned int x = a[i];
        for (int k = 31; ~k; k--) {
            if (!(x >> k) & 1) continue;
            if (r[k]) {
                x ^= r[k];
            } else {
                r[k] = x;
                break;
            }
        }
    }
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%u", &a[i]);
    }
    Gauss(n);
    for (int i = 0; i < 32; i++) {
        if (r[i]) printf("%u\n", r[i]);
    }
    return 0;
}
/*
线性基是一种擅长处理异或问题的数据结构.设值域为 [1,N] ,就可以用一个长度为 \lceil \log_2N 
的数组来描述一个线性基。特别地,线性基第 i 位上的数二进制下最高位也为第 i 位。

一个线性基满足,对于它所表示的所有数的集合 S , S中任意多个数异或所得的结果均能表示为
线性基中的元素互相异或的结果,即意,线性基能使用异或运算来表示原数集使用异或运算能表
示的所有数。运用这个性质,我们可以极大地缩小异或操作所需的查询次数。

插入和判断
我们考虑插入的操作,令插入的数为 x ,考虑 x 的二进制最高位 i ,

若线性基的第 i 位为 00 ,则直接在该位插入 x ,退出;
若线性基的第 i位已经有值 a[i],则x=x⊕a[i],重复以上操作直到 x=0.
如果退出时 x=0,则此时线性基已经可以表示原先的 x了;反之,则说明为了表示 x ,往线性基中加
入了一个新元素。

很容易证明这样复杂度为log_2也可以用这种方法判断能否通过原数列异或得到一个数 x 。

void insert(int x){
    for(reg int i=I;~i;i--)
        if(x&(1ll<<i))
            if(!a[i]){a[i]=x;return;}
            else x^=a[i];
    flag=true;
}
bool check(int x){
    for(reg int i=I;~i;i--)
        if(x&(1ll<<i))
            if(!a[i])return false;
            else x^=a[i];
    return true;
}
查询异或最值

查询最小值相对比较简单。考虑插入的过程,因为每一次跳转操作, x 的二进制最高位必定单调降低
,所以不可能插入两个二进制最高位相同的数。而此时,线性基中最小值异或上其他数,必定会增大。
所以,直接输出线性基中的最小值即可。

考虑异或最大值,从高到低遍历线性基,考虑到第 i位时,如果当前的答案 x第 i位为 0,就
将 x异或上a[i]则不做任何操作。显然,每次操作后答案不会变劣,最终的 x 即为答案。

同样,我们考虑对于一个数 x,它与原数列中的数异或的最值如何获得。用与序列异或最大值类似
的贪心即可解决。

查询 k小值
我们考虑进一步简化线性基。显然,一个线性基肯定可以表示为若干个形如 2^i的数。从高到低处理线
性基每一位,对于每一位向后扫,如果当前数第 i 位为 0,且线性基第 i 位不为0 ,则将当前数异或
上 a[i]这一操作可以在 O(n^2的时间内解决。

经过这一步操作后,设线性基内共有 cnt个数,则它们共可以表示出 2^cnt个数。当然,对于0 必须特
殊考虑。如果插入的总数 n与 cnt相等,就无法表示 0了。

同样,考虑最小值时,也必须要考虑到 00 的情况。事实上,如果插入时出现了未被加入的元素,
就肯定可以表示出 0。随后,我们考虑将 k二进制拆分,用与快速幂类似的方法就可以求出第 k大值。

学过线性代数的同学应该可以看出,这个过程就是对一个矩阵求解异或意义下的秩的过程。因此, cnt \leq \lceil \log_2N \rceilcnt≤?log 
2
?	 N? 一定成立。而最终,线性基中保存的也是异或意义下的一组极小线性无关组。

同样,有关线性基的一切运算都可以看做矩阵的初等行列变换,也就可以将其看做线性规划问题。
同样,可以离线使用高斯消元来构造极小线性基。

bool flag;//可以表示0
int qmax(int res=0){
    for(reg int i=I;~i;i--)
        res=max(res,res^a[i]);
    return res;
}
int qmin(int res=0){
    if(flag)return 0;
    for(reg int i=0;i<=I;i++)
        if(a[i])return a[i];
}
int query(int k){
    reg int res=0;reg int cnt=0;
    k-=flag;if(!k)return 0;
    for(reg int i=0;i<=I;i++){
        for(int j=i-1;~j;j--)
            if(a[i]&(1ll<<j))a[i]^=a[j];
        if(a[i])tmp[cnt++]=a[i];
    }
    if(k>=(1ll<<cnt))return -1;
    for(reg int i=0;i<cnt;i++)
        if(k&(1ll<<i))res^=tmp[i];
    return res;
}
代码*//*
reference:
	
translation:
	
solution:

trigger:
	
note:
	*
date:
	2019.08.03
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define dwn(i,a,b) for(int i=a;i>=b;--i) 
template <typename T> void rd(T &x){x=0;char c=getchar();int f=0;while(!isdigit(c)){f|=c=='-';c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}x=f?-x:x;} 
#define mem(a,b) memset(a,b,sizeof(a))
#define reg register

const int I=60;
int a[61],tmp[61];
int n,x;
bool flag;

inline void ins(int x){
	dwn(i,I,0)
        if(x&(1ll<<i))
            if(!a[i]){a[i]=x;return;}
            else x^=a[i];
    flag=true;
}

inline bool check(int x){
    for(int i=I;~i;i--)
        if(x&(1ll<<i))
            if(!a[i])return false;
            else x^=a[i];
    return true;
}

inline int qmax(int res=0){
    dwn(i,I,0)
        res=max(res,res^a[i]);
    return res;
}

inline int qmin(){
    if(flag)return 0;
    rep(i,0,I)
        if(a[i])return a[i];
}

inline int query(int k){
    int res=0,cnt=0;
    k-=flag;
	if(!k)return 0;
    rep(i,0,I){
    	dwn(j,i-1,0)
            if(a[i]&(1ll<<j))a[i]^=a[j];
        if(a[i])tmp[cnt++]=a[i];
    }
    if(k>=(1ll<<cnt))return -1;
    rep(i,0,cnt-1)
        if(k&(1<<i))res^=tmp[i];
    return res;
}

#undef int
int main(){
#define int long long
	#ifdef WIN32
	freopen("xxj.txt","r",stdin);
	#endif
	rd(n);
	rep(i,1,n){
		rd(x);
		ins(x);
	}
    printf("%lld\n",qmax());
    return 0;
}

BSGS 用于解决高次同余方程的最小整数解,主要思想是分块。


引入

求出下面方程的最小非负整数解 x:

ax≡b(modp)(gcd(a,p)=1)

此处的未知数 x 在指数上,我们就要用到 BSGS(大步小步法,即 Baby Step Giant Step)算法。


思想

BSGS 的本质是分块的思想,我们设 m=⌈p⌉(注意是上取整),又设 x=i×m−j,其中 0≤i≤m,0≤j<m;我们可以将原方程转化为:

ai×m−j≡b(modp)

在 x=i×m−j 中,i 即为大步,j 即为小步;我们将 j 移到方程的右边得到:

ai×m≡b×aj(modp)

观察到 j 只有 O(m) 种取值,所以我们对于每个 j,把 b×ajmodp 的值记录下来,存放在哈希表中(有没有一种分块打表的感觉?)。然后枚举左半边的 i,将对应的值在哈希表中查找对应的 j,如果能找到对应的 j 那么说明已经找到答案了。

对于插入哈希表中的 j 的值,显然在同一个值时,j 的值越大越好,那么直接覆盖即可,无需关注细节问题!

注意:代码中必须要判断无解情况!(本来就无解,或者没有找到合法解)

时间复杂度:O(p)

最长的等差子串的长度

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int T,n,m,len,a[20];
ll l,r,dp[20][15][25][25][20];
ll dfs(int pos,int pre,ll st,ll sum,int d,int lead,int limit)
//pos搜到的位置
//pre前一位数
//st当前公差最大差值
//sum整个数字的最大价值
//d共差
//lead判断是否有前导0
//limit判断是否有最高位限制
{
    if(pos>len) return sum;//dp结束 
    //记录状态(计划搜索)
    //注意d有负数,最小可以到-9,所以记录时数组下标是d+10 
    if((dp[pos][pre][st][sum][d+10]!=-1&&(!limit)&&(!lead))) return dp[pos][pre][st][sum][d+10]; 
    ll ret=0;
    int res=limit?a[len-pos+1]:9;//最高位最大值 
    for(int i=0;i<=res;i++)
    {
        //有前导0且这位也是前导0,一切不变只有位数变化 
        if((!i)&&lead) ret+=dfs(pos+1,0,0,0,-38,1,limit&&(i==res));
        //有前导0但这位不是前导0(这位是数字的最高位)开始有前一位,一个数形成等差数列 
        else if(i&&lead) ret+=dfs(pos+1,i,1,1,-38,0,limit&&(i==res));
        //之前刚搜到1位还没有共差,两位数形成等差数列,记录共差 
        else if(d<-9) ret+=dfs(pos+1,i,2ll,2ll,i-pre,0,limit&&(i==res));
        //搜到2位以后,共差若与前两位相同当前等差数列长度增加,若公差变化则更新整个数字的最大价值,等差数列长度又变为2 
        else if(d>=-9) ret+=dfs(pos+1,i,(i-pre==d)?st+1:2,max(sum,(i-pre==d)?st+1:2),(i-pre==d)?d:i-pre,0,limit&&(i==res));
    }
    //没有前导0和最高限制时可以直接记录当前dp值以便下次搜到同样的情况可以直接使用。 
    return (!limit&&!lead)?dp[pos][pre][st][sum][d+10]=ret:ret;
}
ll part(ll x)
{
    len=0;
    while(x) a[++len]=x%10,x/=10;
    memset(dp,-1,sizeof dp);
    return dfs(1,0,0,0,-38,1,1);//-38是随便赋的其实赋成-10就行了…… 
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld",&l,&r);
        //l是0的时候要特别注意!
        printf("%lld\n",l?(part(r)-part(l-1)):(part(r)-part(l)+1));
    }
    return 0;
}

P1439 【模板】最长公共子序列

把 P1 重新标号为 1 到 n ,P2 进行相应的重标号。 那么 P1 的任意一个子序列都是一个单增序列,且任意一个单增序列都 是 P1 的子序列。 那么只需要对重标号后的 P2 求最长上升子序列即可(因为这样的子序列一定是P1的子序列)。

const int N=1e5+10;
int a[N],b[N],f[N],len;
map<int,int>mp;
int n;

int main(){
	rd(n);
	rep(i,1,n){
		rd(a[i]);
		mp[a[i]]=i;
	}
	rep(i,1,n){
		int x;rd(x);
		b[i]=mp[x];
	}
	f[++len]=b[1];
	rep(i,2,n){
		if(f[len]<b[i])f[++len]=b[i];
		else{
			int p=lower_bound(f+1,f+len+1,b[i])-f;
			f[p]=b[i];
		}
	}
	printf("%d",len);
    return 0;
}

P4290 [HAOI2008] 玩具取名

f[l][r][op]区间l->r可以用op表示出来 目标是求 f 1,n,op ,其中 op ∈ {W, I, N, G} 。

按区间长度从小到大的顺序进行 dp。

对于 f l,r,op ,枚举第一个字母(也 就是 op )变形成的两个字母是什么,及分别对应 [l,r] 中的哪些位置, 即可利用之前的 dp 信息求出。

const int N=210;
bool f[N][N][5];//f[l][r][op]区间l->可以用op表示出来
bool G[5][5][5];//G[i][j][k] 字符i 和 字符j 可以拼出字符 k
int num[N];
int n;
char s[N];
map<char,int>mp;

int main() {
	#ifdef WIN32
	freopen("a.txt","r",stdin);
	#endif
	mp['W']=1,mp['I']=2,mp['N']=3,mp['G']=4;
	rep(i,1,4)rd(num[i]);
	rep(i,1,4)
		rep(j,1,num[i]){
			char a,b;cin>>a>>b;
			G[mp[a]][mp[b]][i]=1;
		}
	scanf("%s",s+1);
	n=strlen(s+1);
	rep(i,1,n)f[i][i][mp[s[i]]]=1;
	rep(len,2,n)
		rep(l,1,n-len+1){
			int r=l+len-1;
			rep(k,l,r-1)//枚举断点
				rep(z,1,4)
					rep(z1,1,4)
						rep(z2,1,4)
							f[l][r][z] |= G[z1][z2][z] & f[l][k][z1] &f[k+1][r][z2];
		}
	bool flag=0;
	if(f[1][n][1]){flag=1;printf("W");}
	if(f[1][n][2]){flag=1;printf("I");}
	if(f[1][n][3]){flag=1;printf("N");}
	if(f[1][n][4]){flag=1;printf("G");}
	if(!flag)puts("The name is wrong!");
    return 0;
}

posted @ 2019-11-03 09:26  设计涉及社稷  阅读(155)  评论(0编辑  收藏  举报