@happyforever
2018-11-03T08:53:37.000000Z
字数 17852
阅读 530
解题报告 六校联考
期望得分:
实际得分:
就。。。没培养出来看到二叉树立马反应遍历顺序。
(上次做二叉树相关题目应该是半年前)
从CF搬运过来的
luogu也有原题,《无尽的生命》
之前做过这个题但是当时也没有去学正解做法(敲完暴力跑路了)
写完前两个题之后还剩大概分钟,就直接上了暴力,没有考虑优化

/** 选某个节点x的话需要满足w[x] < min(min_left[x],min_right[x])* 若某个节点的左子树中有点那么min_left > max_right** 对树上每个节点维护其左子树的最小最大值以及右子树的最大最小值** 一条链那个描述有问题吧。。。。。*/#include <cstdio>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}inline int max(int x,int y){return x>y?x:y;}inline int min(int x,int y){return x<y?x:y;}const int N=1e5+1,inf=2147483647;int n,w[N],minn[N],maxn[N],son[2][N],siz[N],ans=1;int fa[N],f[N];/*void dfs(int now){int left=son[0][now],right=son[1][now];minn[now]=maxn[now]=w[now],siz[now]=1;if(!left && !right)return ;dfs(left),dfs(right);siz[now]+=siz[left]+siz[right];minn[now]=min(min(minn[left],minn[right]),w[now]);maxn[now]=max(max(maxn[left],maxn[right]),w[now]);}*/void dfs(){int now=1,cnt=0;while(now){fa[++cnt]=w[now];if(son[0][now])now=son[0][now];else now=son[1][now];}}inline void work2(){for(int i=1;i<=9;++i)f[i]=1;for(int maxx,i=2;i<=n;++i){maxx=0;for(int j=1;j<i;++j)if(fa[j]<fa[i] && f[j]>maxx)maxx=f[j];if((f[i]=maxx+1)>ans)ans=f[i];}}bool vis[11];void work(int now){int left=son[0][now],right=son[1][now];minn[now]= inf,maxn[now]=-inf,siz[now]=0;if(vis[now]){minn[now]=maxn[now]=w[now];siz[now]=1;}if( left)work(left);if(right)work(right);minn[now]=min(min(minn[left],minn[right]),minn[now]);maxn[now]=max(max(maxn[left],maxn[right]),maxn[now]);siz[now]+=siz[left]+siz[right];}inline bool check(){work(1);/*if(vis[1] && vis[3] && vis[5]){for(int i=1;i<=n;++i)printf("%d ",vis[i]?1:0);puts("");for(int i=1;i<=n;++i)printf("%d %d %d\n",siz[i],minn[i],maxn[i]);}*/for(int i=1;i<=n;++i)if(minn[son[0][i]]<=maxn[son[1][i]] || (vis[i] && w[i]>=minn[son[1][i]]))return false;// puts("GG");return true;}void meiju(int now){if(now>n){if(check())ans=max(ans,siz[1]);return ;}vis[now]=true;meiju(now+1);vis[now]=false;meiju(now+1);}int main(){freopen("point.in","r",stdin);freopen("point.out","w",stdout);n=read();minn[0]=inf,maxn[0]=-inf;for(int i=1;i<=n;++i)w[i]=read();for(int i=1;i<=n;++i)son[0][i]=read(),son[1][i]=read();if(n<=100)meiju(1);else work2();printf("%d",ans);fclose(stdin);fclose(stdout);return 0;}

/** luogu原题吧。。。* 40分可以归并排序求* 20分特判* 40分离散化+树状数组逆序对*/#include <cstdio>#include <iostream>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=1e5+1;int n,a[N],emp[N];long long ans;void mergesort(int l,int r){if(l==r)return ;int mid=l+r>>1;mergesort(l,mid);mergesort(mid+1,r);int i=l,j=mid+1,k=l;while(i<=mid && j<=r){if(a[i]<a[j])emp[k++]=a[i++];else ans+=mid-i+1,emp[k++]=a[j++];}while(i<=mid)emp[k++]=a[i++];while(j<=r)emp[k++]=a[j++];for(int x=l;x<=r;++x)a[x]=emp[x];}void work1(){int x=read(),y=read();if(x>y)std::swap(x,y);printf("%d",(y-x+1)*2-3);}void work2(){/** 两种情况:互相包含,互相交叉* 假设a<x<y<b* 相互包含那么答案为(b-a)+(b-a-1)+(y-x)+(y-x-1)* 化简得:(b-a+y-x-1)*2** 假设a<x<b<y* 相互交叉那么答案为(b-a)+(b-a-1)+(y-x-1)+(y-x-2)* 化简得:(b-a+y-x-2)*2*/int a=read(),b=read(),x=read(),y=read();if(a>b)std::swap(a,b);if(x>y)std::swap(x,y);long long ans=b-a+y-x-1;if(y>b)--ans;std::cout<<ans*2;}int main(){freopen("a.in","r",stdin);freopen("a.out","w",stdout);n=read();int maxn=0;if(n==1)return work1(),0;if(n==2)return work2(),0;for(int i=1;i<=N;++i)a[i]=i;for(int x,y,i=1;i<=n;++i){x=read(),y=read();if(x>y)std::swap(x,y);std::swap(a[x],a[y]);if(y>maxn)maxn=y;}/*for(int i=1;i<=maxn;++i)printf("%d ",a[i]);puts("");*/mergesort(1,maxn);/*for(int i=1;i<=maxn;++i)printf("%d ",a[i]);puts("");*/// printf("%d",ans);std::cout<<ans;fclose(stdin);fclose(stdout);return 0;}

/** 每次将一个编号变成另一个编号* 考虑对整张图维护按照编号建图* 当编号改变时从所有这个编号的点出发向四周bfs,重新建图*/#include <queue>#include <cstdio>#include <cstring>#include <algorithm>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=200001;int n,head[N],tot,a[N];struct Edge{int v,nxt;}edge[N<<1];/*struct Node{int id,col;bool operator<(const Node &gg)const{return col<gg.col;}bool operator<(const int &x)const{return col<x;}}a[N];*/inline void add(int v,int u){edge[++tot]=(Edge){v,head[u]};head[u]=tot;edge[++tot]=(Edge){u,head[v]};head[v]=tot;}bool vis[N];int ans;void dfs(int now,int fa){vis[now]=true;for(int v,i=head[now];i;i=edge[i].nxt)if((v=edge[i].v)!=fa && a[v]==a[now])dfs(v,now);}void bfs(int y,int x){// while(!q.empty())q.pop();// int now=std::lower_bound(a+1,a+1+n,x)-a;if(ans==1)goto E;ans=0;memset(vis,false,sizeof vis);for(int i=1;i<=n;++i)if(a[i]==x)a[i]=y;for(int i=1;i<=n;++i)if(!vis[i])++ans,dfs(i,i);E: printf("%d\n",ans);}int main(){freopen("simulator.in","r",stdin);freopen("simulator.out","w",stdout);n=read();int q=read();// for(int i=1;i<=n;++i)a[i]=(Node){i,read()};for(int i=1;i<=n;++i)a[i]=read();for(int i=1;i<n;++i)add(read(),read());/* std::sort(a+1,a+1+n);for(int i=1;i<=n;++i)if(a[i].col!=a[i-1].col)++ans;*/while(q--)bfs(read(),read());fclose(stdin);fclose(stdout);return 0;}
选根节点的话,在这棵子树内选的其他的点都要比根节点的值大
如果在左子树选了一个点,在右子树中选的其他点要比它小
有10%的特殊数据有性质是所有节点都没有右子树或者都没有左子树,也就是说只需要维护第一条限制
此时显然是一个。。。最长上升子序列
首先对整棵树先遍历根节点再遍历右子树再遍历左子树
而后对这个dfs序跑最长上升子序列
#include <cstdio>#include <algorithm>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=1e5+1,inf=2147483647;int n,cnt,ls[N],rs[N],a[N],w[N];void dfs(int now){if(!now)return ;a[++cnt]=w[now];dfs(rs[now]);dfs(ls[now]);}int sta[N],len;int main(){freopen("point.in","r",stdin);freopen("point.out","w",stdout);n=read();for(int i=1;i<=n;++i)w[i]=read();for(int i=1;i<=n;++i)ls[i]=read(),rs[i]=read();dfs(1);/*for(int i=1;i<=n;++i)printf("%d ",a[i]);*/sta[++len]=a[1];for(int i=2;i<=n;++i){if(a[i]>sta[len])sta[++len]=a[i];else{int j=std::lower_bound(sta+1,sta+1+len,a[i])-sta;sta[j]=a[i];}}printf("%d",len);fclose(stdin);fclose(stdout);return 0;}
首先,将交换过的数离散化,这样值域就和个数同阶
总的答案可以成两部分算:
一部分是位置改变的。
我们可以用树状数组维护
另一部分是位置没有改变的
这样每个数都在原来的位置上,我们首先处理出哪些元素没有改变,用前缀和维护
表示小于 的元素中没有出现过的有几个
这样每次将 的前缀和 和 当前数的前缀和做差即可
#include<cstdio>#include<algorithm>#include<iostream>#define LL long long#define lb(x) (x & (-x))#define Pair pair<int, int>#define fi first#define se second#define MP(x, y) make_pair(x, y)using namespace std;const int MAXN = 1e6 + 10;inline int read() {char c = getchar(); int x = 0, f = 1;while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();return x * f;}int N, T[MAXN], date[MAXN], pos[MAXN];// pos[i] i位置的元素 date 离散化数组LL sum[MAXN];Pair P[MAXN];void add(int x) {for(int i = x; i <= N * 2; i += lb(i)) T[i]++;}LL Query(int x) {LL ans = 0;for(int i = x; i; i -= lb(i)) ans += T[i];return ans;}int main() {freopen("a.in","r", stdin); freopen("a.out", "w", stdout);N = read();for(int i = 1; i <= N; i++) {P[i].fi = read(), P[i].se = read();date[i] = P[i].fi; date[i + N] = P[i].se;pos[i] = i; pos[i + N] = i + N;}sort(date + 1, date + N * 2 + 1);int num = unique(date + 1, date + N * 2 + 1) - date - 1;for(int i = 1; i <= num; i++) sum[i] = sum[i - 1] + date[i] - date[i - 1] - 1;for(int i = 1; i <= N; i++) {P[i].fi = lower_bound(date + 1, date + num + 1, P[i].fi) - date;P[i].se = lower_bound(date + 1, date + num + 1, P[i].se) - date;swap(pos[P[i].fi], pos[P[i].se]);}LL ans = 0;for(int i = num; i >= 1; i--) {ans += Query(pos[i]);ans += abs(sum[pos[i]] - sum[i]);add(pos[i]);}cout << ans << "\n";return 0;}
我们发现给定我们树上的所有节点的颜色,我们可以求得军团个数,方法是进行dfs,如果一个结点和他父亲节点颜色不同,答案就
然后暴力更改暴力统计答案
我们可以优化一下上面那个暴力,用vector纪录下每种颜色存在的位置,然后枚举要更改的颜色位置,每个位置原来对于答案的贡献是和他相邻不同色的节点个数,我们减去原来的答案加上更改后的答案就得到了当前的答案。然后再暴力合并vector就行了
我们考虑优化合并操作,我们发现把变成和把变成得到的答案是相同的,所以我们在合并的时候以及更改颜色的时候都是把颜色少的改成颜色多的,并且维护每种颜色实际的vector是哪个,然后就相当于启发式合并。
每个点的复杂度就是其度数乘以它被统计答案的次数,最差情况下每个点也只会被统计次答案,那么复杂度就是
也就等于
#include<cstdio>#include<algorithm>#include<cstring>#include<queue>#include<iostream>#include<set>#include<map>#define ll long long#define M 200010using namespace std;int read() {int nm = 0, f = 1;char c = getchar();for(; !isdigit(c); c = getchar()) if(c == '-') f = -1;for(; isdigit(c); c = getchar()) nm = nm * 10 + c - '0';return nm * f;}int note[M], sz[M], cor[M], id[M];vector<int>to[M], to1[M];int n, q, ans;void dfs(int now, int fa) {if(cor[now] != 0 && cor[now] != cor[fa]) ans++;for(int i = 0; i < to[now].size(); i++) {int vj = to[now][i];if(vj == fa) continue;dfs(vj, now);}}void del(int x) {for(int i = 0; i < to[x].size(); i++) {int vj = to[x][i];if(cor[vj] != cor[x]) ans--;}}void insert(int x) {for(int i = 0; i < to[x].size(); i++) {int vj = to[x][i];if(cor[vj] != cor[x]) ans++;}}int tot = 0, tot2 = 0;int main() {freopen("simulator.in", "r", stdin), freopen("simulator.out", "w", stdout);n = read(), q = read();for(int i = 1; i <= n; i++) cor[i] = read(), sz[cor[i]]++, to1[cor[i]].push_back(i), id[i] = i, note[i] = i;for(int i = 1; i < n; i++) {int vi = read(), vj = read();to[vi].push_back(vj), to[vj].push_back(vi);}to[1].push_back(0), cor[0] = 0x3e3e3e3e;dfs(1, 0);while(q--) {int x = read(), y = read();int xn = id[x], yn = id[y];if(sz[xn] < sz[yn]) {tot += sz[xn], tot2 += to1[xn].size();for(int i = 0; i < to1[xn].size(); i++) {int op = to1[xn][i];del(op);to1[yn].push_back(op);}for(int i = 0; i < to1[xn].size(); i++) {int op = to1[xn][i];cor[op] = yn;}for(int i = 0; i < to1[xn].size(); i++) {int op = to1[xn][i];insert(op);}to1[xn].clear();sz[yn] += sz[xn];sz[xn] = 0;id[x] = 0;} else {tot+=sz[yn], tot2 += to1[yn].size();for(int i = 0; i < to1[yn].size(); i++) {int op = to1[yn][i];del(op);to1[xn].push_back(op);}for(int i = 0; i < to1[yn].size(); i++) {int op = to1[yn][i];cor[op] = xn;}for(int i = 0; i < to1[yn].size(); i++) {int op = to1[yn][i];insert(op);}to1[yn].clear();sz[xn] += sz[yn];sz[yn] = 0;id[y] = xn;id[x] = 0;}cout << ans << "\n";}return 0;}
期望得分:
实际得分:
开场分钟切掉,爽翻
然后直接忽略数据范围看去了
然后。。
有一部分数据是所有
那么这个循环就会一直跑到RE才停下来
那就。。。挂了
暴力范围给错了,特殊性质看串了行,就。。。没做考虑
图里面有环,我的解决办法是设置数组,但是dfs的时候忘了回溯就。。。了

/** 考前知道是gxb出的这道题。。* 于是7:30的时候反复问了他两遍:你T1是个水题吧* 是* en* 哇,瞬间心安** 开始考试15分钟之后切掉* 爽了*/#include <cstdio>#include <algorithm>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=1e5+1;int n,s,a[N];int main(){freopen("median.in","r",stdin);freopen("median.out","w",stdout);n=read(),s=read();for(int i=1;i<=n;++i)a[i]=read();std::sort(a+1,a+1+n);long long ans=0;for(int i=n/2+1;a[i]<s;++i)ans+=s-a[i];printf("%I64d",ans);fclose(stdin);fclose(stdout);return 0;}

/** 他这暴力范围给错了吧。。。** 题目实际相当于给你2n个位置,在其中挑n个位置放东西* 若将没放东西的位置看做+1,放了东西的位置看做-1,要求对任意位置的前缀和小于k** 若k>n,ans=C(2n,n)* 设dp[i][j]表示第i天,前缀和为j,但是负数没法考虑* dp[i][j]=dp[i-1][j-1]*2+dp[i-1][j+1]*2** 若k==1那么就是括号序列的合法方案数,即Catalan数*/#include <cstdio>/*inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}inline int ksm(long long x,int b){long long res=1;for(;b;b>>=1,x=x*x%p)if(b&1)res=res*x%p;return res;}*/const int N=1e3+1;int n,m,k,p,dp[N][N],ans;int exgcd(int a,int b,int &x,int &y){if(!b){x=1,y=0;return a;}int t=exgcd(b,a%b,y,x);y-=(a/b)*x;return t;}void work1(){long long ans,emp=1,tmp=1;int i,x,y;for(i=2;i<=n;++i)emp=emp*i%p;n<<=1;for(;i<=n;++i)tmp=tmp*i%p;exgcd(tmp,p,x,y);if(x<0)x+=p;printf("%d",emp*x%p);}bool a[N];void dfs(int now,int sum,int last){if(now>m){if(last==0){++ans;/*for(int i=1;i<=m;++i)printf("%d ",a[i]?1:0);puts("");*/if(ans==p)ans=0;}return ;}if(sum<k-1)//不写作业{a[now]=false;dfs(now+1,sum+1,last);}if(last>0)//写作业{a[now]=true;dfs(now+1,sum-1,last-1);}}long long work2(int h){if(h==1)return 1;if(h==2)return 2;if(h==3)return 5;return (work2(h-1)*(4*h-2)%(p*(h+1)))/(h+1)%p;}int main(){freopen("term.in","r",stdin);freopen("term.out","w",stdout);scanf("%d%d%d",&n,&k,&p);if(k>n)work1();elseif(n<=15){m=n<<1;dfs(1,0,n);printf("%d\n",ans);}elseif(k==1)printf("%d",work2(n));else ;E: fclose(stdin);fclose(stdout);return 0;}

/**/#include <cstdio>#include <cstring>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=200001,inf=2147483647;int tot,n,m,s,t,ans=-1,val[N],head[N],fa[N],fa_dis[N];bool vis[N];struct Edge{int v,w,nxt;}edge[N];void add(int w,int v,int u){edge[++tot]=(Edge){v,w,head[u]};head[u]=tot;fa[v]=u,fa_dis[v]=w;}void dfs(int now,int w){// printf("%d %d\n",now,w);w&=val[now];vis[now]=true;if(now==t){if(w<ans || ans==-1)ans=w;return ;}for(int v,i=head[now];i;i=edge[i].nxt)if(!vis[v=edge[i].v])dfs(v,w&edge[i].w);}inline void work(){int x=inf;while(s!=t && t){x&=fa_dis[t]&val[t];t=fa[t];}if(s==t)ans=val[s]&x;}int main(){freopen("rabbit.in","r",stdin);freopen("rabbit.out","w",stdout);n=read(),m=read(),s=read(),t=read();for(int i=1;i<=n;++i)val[i]=read();for(int i=1;i<=m;++i)add(read(),read(),read());if(n<=10)dfs(s,inf);else work();printf("%d",ans);fclose(stdin);fclose(stdout);return 0;}
首先排序
考虑从的位置开始往后统计有多少位置的数,我们只需要修改这些位置的数字就可以了
所以
#include <cstdio>#include <algorithm>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=1e5+1;int n,s,a[N];int main(){freopen("median.in","r",stdin);freopen("median.out","w",stdout);n=read(),s=read();for(int i=1;i<=n;++i)a[i]=read();std::sort(a+1,a+1+n);long long ans=0;for(int i=n/2+1;a[i]<s && i<=n;++i)ans+=s-a[i];printf("%I64d",ans);fclose(stdin);fclose(stdout);return 0;}
题目实际相当于给你个和个,要求对任意位置的前缀和的方案数有多少
的时候答案显然为卡特兰数,所以我们考虑卡特兰数的证明过程
若数列不合法,那么一定存在一个位置,假设在位置前面有个,有个
我们考虑将位置之后的个变成,个变成
那么该序列一共含有个和个
所以不合法的方案数就是
用总方案数减去不合法方案数就是合法方案数了
这样可以拿到分
对于最后的分,注意到模数不一定是质数
考虑对阶乘进行质因数分解,对模数也进行质因数分解,那么就可以通过先把质因数同时除掉之后再进行
或者也可以用扩展卢卡斯定理
#include<cstdio>#include<cstring>#include<iostream>#define int long long#define LL intusing namespace std;const int MAXN = 2 * 1e6 + 10;inline int read() {char c = getchar(); int x = 0, f = 1;while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();return x * f;}int N, mod, prime[MAXN], vis[MAXN], tot, mn[MAXN], num[MAXN], K;void GetPhi(int N) {vis[1] = 1;for(int i = 2; i <= N; i++) {if(!vis[i]) prime[++tot] = i, mn[i] = tot;for(int j = 1; j <= tot && (i * prime[j] <= N); j++) {vis[i * prime[j]] = 1; mn[i * prime[j]] = j;if(!(i % prime[j])) break;}}}void insert(int x, int opt) {while(x != 1) num[mn[x]] += opt, x = x / prime[mn[x]];}int fastpow(int a, int p) {int base = 1;while(p) {if(p & 1) base = (1ll * base * a) % mod;a = (1ll * a * a) % mod; p >>= 1;}return base;}int FindAns() {LL ans = 1;for(int i = 1; i <= tot; i++)if(num[i])ans = (1ll * ans * fastpow(prime[i], num[i])) % mod;return ans;}main() {freopen("term.in", "r", stdin);freopen("term.out", "w", stdout);N = read(); K = read(); mod = read();GetPhi(2 * N);for(int i = N + 1; i <= 2 * N; i++) insert(i, 1);for(int i = 1; i <= N; i++) insert(i, -1);LL ans1 = FindAns();memset(num, 0, sizeof(num));for(int i = N + K + 1; i <= 2 * N; i++) insert(i, 1);for(int i = 1; i <= N - K; i++) insert(i, -1);LL ans2 = FindAns();cout << (ans1 - ans2 + mod) % mod;return 0;}
缩点然后反着跑SPFA有分
注意的部分分并不是保证给定的是一棵树,可能是很多个联通块并且每个联通块里面可能有环
对于的数据可以考虑在每个联通块中维护当前联通块所有边权和点权的是多少,那么问题转化成DAG上的问题
考虑从高位向低位枚举二进制位,贪心策略是希望高位上能够为,那么我们考虑在整个DAG中的那些路径是能够使这一位为的,显然,在所有从到的道路中,若存在某个点或者边,那么他所有的后继路径和前驱路径都是可行的,那么我们可以正反拓扑两次找到所有可行的点边集合作为当前答案。
然后考虑下一位,下一位中因为要保证上一位是,所以能够走的点和边肯定是上次得到的点边集合里的,我们在这个得到的集合中在尽行同样的拓扑看看当前位是否可行,如果可行的话就用新得到的点边集合去更新原图的点边集合。
如此做三十次考虑所有的位就能得到答案了。
#include<cstdio>#include<algorithm>#include<cstring>#include<queue>#include<iostream>#include<ctime>#include<cmath>#include<set>#include<map>#define ll long long#define M 700010struct Edge {int vi, vj, v;} edge[M];struct Note {int vj, v, id;} be;using namespace std;int read() {int nm = 0, f = 1;char c = getchar();for(; !isdigit(c); c = getchar()) if(c == '-') f = -1;for(; isdigit(c); c = getchar()) nm = nm * 10 + c - '0';return nm * f;}vector<int>to[M];vector<Note> to1[M], to2[M];int belong[M], st[M], dfn[M], low[M], dft, n, m, s, t, note[M], noww[M], tp, tot, sz[M], ans = 2147483647, rudu1[M], rudu2[M];bool vis[M];void tarjan(int now) {vis[now] = true;st[++tp] = now;dfn[now] = low[now] = ++dft;for(int i = 0; i < to[now].size(); i++) {int vj = to[now][i];if(!dfn[vj]) {tarjan(vj);low[now] = min(low[now], low[vj]);} else if(vis[vj]) low[now] = min(low[now], dfn[vj]);}if(dfn[now] == low[now]) {tot++;while(st[tp] != now) {int x = st[tp];vis[x] = false;noww[tot] &= note[x];belong[x] = tot;tp--;}int x = st[tp];vis[x] = false;noww[tot] &= note[x];belong[x] = tot;tp--;}}bool can[M], tmp1[M], tmp2[M];int note1[M], note2[M], tp1, tp2;void tuopu() {queue<int>q;q.push(belong[t]);while(!q.empty()) {int op = q.front();q.pop();for(int i = 0; i < to2[op].size(); i++) {int vj = to2[op][i].vj;rudu2[vj]++;if(!vis[vj]) q.push(vj), vis[vj] = true;}}while(!q.empty()) q.pop();q.push(belong[s]);while(!q.empty()) {int op = q.front();note1[++tp1] = op;q.pop();for(int i = 0; i < to1[op].size(); i++) {int vj = to1[op][i].vj;rudu1[vj]--;if(rudu1[vj] == 0) q.push(vj);}}q.push(belong[t]);while(!q.empty()) {int op = q.front();q.pop();note2[++tp2] = op;for(int i = 0; i < to2[op].size(); i++) {int vj = to2[op][i].vj;rudu2[vj]--;if(rudu2[vj] == 0) q.push(vj);}}}void work(int x) {for(int i = 1; i <= tot; i++) tmp1[i] = tmp2[i] = false;if((noww[belong[s]] & x) == 0) tmp1[belong[s]] = true;for(int i = 1; i <= tp1; i++) {int op = note1[i];for(int j = 0; j < to1[op].size(); j++) {be = to1[op][j];if((tmp1[op] == true) || (can[be.id] && ((be.v & x) == 0))) {tmp1[be.vj] = true;tmp1[be.id] = true;} else if((noww[be.vj] & x) == 0 && (can[be.vj])) tmp1[be.vj] = true;}}if((noww[belong[t]] &x) == 0) tmp2[belong[t]] = true;for(int i = 1; i <= tp2; i++) {int op = note2[i];for(int j = 0; j < to2[op].size(); j++) {be = to2[op][j];if((tmp2[op] == true) || (can[be.id] && ((be.v & x) == 0))) {tmp2[be.vj] = true;tmp2[be.id] = true;} else if((noww[be.vj] & x) == 0 && (can[be.vj])) tmp2[be.vj] = true;}}for(int i = 1; i <= tot; i++) tmp1[i] = (tmp1[i] | tmp2[i]);if(tmp1[belong[s]] && tmp1[belong[t]]) ans -= x;else return;for(int i = 1; i <= tot; i++) can[i] = (can[i] & tmp1[i]);}int main() {freopen("rabbit.in", "r", stdin);freopen("rabbit.out", "w", stdout);cin >> n >> m >> s >> t;for(int i = 1; i <= n; i++) note[i] = read(), noww[i] = 2147483647;for(int i = 1; i <= m; i++) {int vi = read(), vj = read(), v = read();edge[i].vi = vi, edge[i].vj = vj, edge[i].v = v;to[vi].push_back(vj);}tarjan(s);if(!dfn[t]) {puts("-1");return 0;}for(int i = 1; i <= m; i++) {int vi = edge[i].vi, vj = edge[i].vj, v = edge[i].v;vi = belong[vi], vj = belong[vj];if(vi == 0 || vj == 0) continue;if(vi == vj) noww[vi] &= v;else {be.id = ++tot;be.vj = vj;be.v = v;to1[vi].push_back(be);rudu1[vj]++;be.vj = vi;to2[vj].push_back(be);}}if(belong[s] == belong[t]) {cout << noww[belong[s]] << "\n";return 0;}tuopu();for(int i = 1; i <= tot; i++) can[i] = true;for(int i = 30; i >= 0; i--) work(1 << i);cout << ans << "\n";return 0;}