【Codeforces 453A】Little Pony and Expected Maximum

题目传送门:http://codeforces.com/problemset/problem/453/A

这个题目,为什么我又感觉很神QAQ
题解看这里:http://www.cnblogs.com/qscqesze/p/4411069.html

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdio>
#include<cmath>
#define LL long long
#define abs(x) ((x)>0?(x):-(x))
using namespace std;

int n,m;

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
	return ret*f;
}

int main(){
	m = read(); n = read(); 
	double p = 1.0 - pow((double)(m-1)/m,n), vout = 0;
	for (int i=1;i<=m;i++) vout += (pow((double)i/m,n)-pow((double)(i-1)/m,n))*i;
	printf("%.10lf\n",vout);
	return 0;
}

又一次看到这个图了,蜜汁喜欢QAQ
o_690e2a0828381f3056038f33a8014c086e06f030

【BZOJ 2318】[SPOJ 4060] game with probability Problem

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2318

网上绝大部分证明完全没有说清楚好吗? (╯‵□′)╯︵┻━┻
只有这一份题解才稍微正常一点:http://codeplay0314.coding.me/2015/07/10/BZOJ2318/
本地备份:http://oi.cyo.ng/wp-content/uploads/2016/08/12345687856456754.png
不过这一题的推式子还是很神的,有一定参考价值。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdio>
#include<cmath>
#include<bitset>
#define LL long long
#define abs(x) ((x)>0?(x):-(x))
using namespace std;

const int N = 1000+9;
const int LIM = 1000;

int n;
double f[N],g[N],p,q;

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
	return ret*f;
}

int main(){
	int T=read(); while (T--) {
		n = min(read(), LIM); scanf("%lf%lf",&p,&q);
		f[0] = 0; g[0] = 1;
		for (int i=1;i<=n;i++) {
			if (f[i-1] > g[i-1]) p = 1 - p, q = 1 - q;
			f[i] = (p*g[i-1] + (1-p)*q*f[i-1]) / (1 - (1-p)*(1-q));
			g[i] = (q*f[i-1] + (1-q)*p*g[i-1]) / (1 - (1-p)*(1-q));
			if (f[i-1] > g[i-1]) p = 1 - p, q = 1 - q;	
		}
		printf("%.6lf\n",f[n]);
	}
	return 0;
}

值得一提的是,我最开始定义的g[i]为胜i个石子时,Bob先走,Bob的胜算是多少
这样写出来的方程是: \(\begin{array}{l}
{a_i} = \left\{ \begin{array}{l}
p(1 – {b_i}) + (1 – p)(1 – {b_{i – 1}}),{b_i} < {b_{i - 1}}\\ p(1 - {b_{i - 1}}) + (1 - p)(1 - {b_i}),{b_i} > {b_{i – 1}}
\end{array} \right.\\
{b_i} = \left\{ \begin{array}{l}
q(1 – {a_i}) + (1 – q)(1 – {a_{i – 1}}),{a_i} < {a_{i - 1}}\\ q(1 - {a_{i - 1}}) + (1 - q)(1 - {a_i}),{a_i} > {a_{i – 1}}
\end{array} \right.
\end{array}\)
但你会发现,这么推,推不出来QAQ
仔细想一想为什么,我觉得是f[i]与(1-f[i])混用了吧?

【Codeforces 442B】Andrey and Problem

题目传送门:http://codeforces.com/contest/442/problem/B
官方题解:http://codeforces.com/blog/entry/12739

此题真的是妙啊! 奥妙重重.jpg
考虑当前已经有一个集合了,不失望的概率是f,设g=Π(1-其中所有人的p)
如果考虑是否加入第i个人,不难得出必须满足一下等式:
\(f \cdot (1 – p) + g \cdot p > f\)
化简得到,如果有优化的可能,则 g > f
然后来比较人选i和人选j
如果i优于j,则有以下不等式:
\(f \cdot (1 – {p_i}) + g \cdot {p_i} > f \cdot (1 – {p_j}) + g \cdot {p_j}\)
化简得到\({p_i} > {p_j}\)
又因为加入顺序明显不影响答案,所以排序之后从大到小贪心即可

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdio>
#include<cmath>
#define LL long long
#define abs(x) ((x)>0?(x):-(x))
using namespace std;

const int N = 100+9;
const double EPS = 1e-8;

int n;
double p[N],f,g=1;

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
	return ret*f;
}

int main(){
	n = read(); for (int i=1;i<=n;i++) scanf("%lf",&p[i]);
	sort(p+1,p+1+n); if (abs(p[n]-1) < EPS) {cout<<1; exit(0);}
	for (int i=n;i;i--) {
		double tmp = f*(1-p[i]) + g*p[i];
		if (tmp > f) f = tmp, g *= (1-p[i]);
		else break;
	} printf("%.10lf",f);
	return 0;
}

【Codeforces 708C】Centroids

题目传送门:http://codeforces.com/problemset/problem/708/C

我是函数式线段树+出栈入栈序的做法(其实一个RMQ即可)
但这题可以O(n)DP!记录子树中,最大的,不超过n/2的值是多少

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define LL long long
#define abs(x) ((x)>0?(x):-(x))
using namespace std;

const int N = 800000+9;
const int M = 20000000;
const int INF = 1000000000;

int n,tot[N],ls[N],sig,rs[N],head[N],to[N],nxt[N],vex[N*2];

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
	return ret*f;
}

namespace Chair_Tree{
	#define CT Chair_Tree
	int sum[M][2],ch[M][2],cnt,root[N],ans_tmp;
	
	void Add(int p, int &w, int l, int r, int pos, int ty) {
		w = ++cnt; ch[w][0] = ch[p][0]; ch[w][1] = ch[p][1]; 
		sum[w][0] = sum[p][0]; sum[w][1] = sum[p][1]; sum[w][ty]++;
		if (l < r) {
			int mid = l + r + 1 >> 1;
			if (pos < mid) Add(ch[p][0],ch[w][0],l,mid-1,pos,ty);
			else Add(ch[p][1],ch[w][1],mid,r,pos,ty);
		}
	} 
	
	inline void modify(int i, int val) {
		if (val > 0) Add(root[i-1],root[i],1,n,val,1);
		else Add(root[i-1],root[i],1,n,-val,0);
	}
	
	void query(int w, int l, int r, int L, int R, int f, int ty){
		if (L <= l && r <= R) ans_tmp += sum[w][ty]*f;
		else {
			int mid = l + r + 1 >> 1;
			if (L < mid) query(ch[w][0],l,mid-1,L,R,f,ty);
			if (R >= mid) query(ch[w][1],mid,r,L,R,f,ty);
		}
	}
	
	inline int query(int l, int r, int L, int R,int ty) {
		if (l > r) return false;
		ans_tmp = 0;
		query(root[l-1],1,n,L,R,-1,ty);
		query(root[r],1,n,L,R,1,ty);
		return ans_tmp;
	}
	
	inline void Merge(){
		for (int i=1;i<=cnt;i++) 
			sum[i][0] = sum[i][1] - sum[i][0];
	}
};

void DFS(int w, int f) {
	tot[w] = 1; ls[w] = ++sig; 
	for (int i=head[w];i;i=nxt[i]) if (to[i] != f)
		DFS(to[i],w), tot[w] += tot[to[i]];
	rs[w] = ++sig; 
	vex[ls[w]] = tot[w];
	vex[rs[w]] = -tot[w];
}

inline void AddEdge(int a, int b) {
	static int T = 0;
	to[++T] = b; nxt[T] = head[a]; head[a] = T;
	to[++T] = a; nxt[T] = head[b]; head[b] = T;
}

inline bool judge(int w) {
	int MX = 0, vx = 0, MN, ty, sta;
	for (int i=head[w];i;i=nxt[i]) 
		if (ls[w] < ls[to[i]] && ls[to[i]] <= rs[w]) {if (MX < tot[to[i]]) MX = tot[to[i]], vx = to[i], ty = 0;} 
		else {int tmp = n - tot[w]; if (MX < tmp) MX = tmp, vx = to[i], ty = 1;}
	if (MX <= n/2) return 1;
	else {
		sta = n/2;
		MN = MX-n/2;
		if (!ty) return CT::query(ls[vx],rs[vx],MN,sta,1);
		else {
			int tmp = CT::query(1,ls[w]-1,MN,sta,1) + CT::query(rs[w]+1,n*2,MN,sta,1);
			tmp -= CT::query(1,ls[w]-1,MN,sta,0); MN = n - MN; sta = n - sta; swap(MN,sta);
			tmp += CT::query(2,ls[w],MN,sta,0);
			return tmp;
		}
	}
}

int main(){
	n = read(); 
	for (int i=1;i<n;i++) AddEdge(read(),read()); DFS(1,1);
	for (int i=1;i<=n*2;i++) CT::modify(i,vex[i]); CT::Merge();
	for (int i=1;i<=n;i++) printf("%d ",judge(i));
	return 0;
}

【Codeforces 700B】Connecting Universities

题目传送门:http://codeforces.com/problemset/problem/700/B
中文体面:http://paste.ubuntu.com/23084330/

好题啊!做了以后心旷神怡!巧啊!妙啊!
题解的话,让我们来召唤官方题解:http://codeforces.com/blog/entry/46283

#include<iostream>
#include<cstdio>
#define LL long long
using namespace std;

const int N = 400000+9;

int n,k,tot,head[N],to[N],nxt[N],tag[N],f[N];
LL vout;

inline void Add_Edge(int a, int b){
	static int T = 0;
	nxt[++T] = head[a]; to[T] = b; head[a] = T;
	nxt[++T] = head[b]; to[T] = a; head[b] = T;
}

inline int read(){
	char c=getchar(); int ret=0;
	while (c<'0'||c>'9') c=getchar();
	while (c<='9'&&c>='0') ret=ret*10+c-'0', c=getchar();
	return ret;
}

void DFS(int w, int fa) {
	f[w] = tot; if (tag[w]) tot++;
	for (int i=head[w];i;i=nxt[i]) 
		if (to[i] != fa) DFS(to[i], w);
	f[w] = tot - f[w];
}

int main(){
	n = read(); k = read();
	for (int i=1,lim=k*2,a,b;i<=lim;i++) tag[read()] = 1;
	for (int i=1;i<n;i++) Add_Edge(read(),read());
	DFS(1,1); for (int i=2;i<=n;i++) vout += min(f[i],2*k-f[i]);
	printf("%I64d\n",vout); 
	return 0;
} 

【Codeforces 707C】Pythagorean Triples

相关链接

题目传送门:http://codeforces.com/contest/707/problem/C

解题报告

这个题目第一眼看到,一脸懵逼,觉得这个东西一定是爆搜
后来想不出来弃疗了

今天再来看,也是没有思路,但看到了第三组样例,便一下子又了思路
假设我们读入a,考虑a^2+b^2=c^2,移相得a^2=(c+b)*(c-b)
如果a^2为奇数,让c-b=1即可
如果a^2为偶数,让c-b=2即可

Code

#include<iostream>
#include<cstdio>
#define LL long long
using namespace std;

LL a,b,c;

int main(){
	cin>>a; a *= a;
	if (a <= 4) cout<<-1;
	else if (a & 1) cout<<a/2<<' '<<a/2+1;
	else cout<<a/4+1<<' '<<a/4-1;
	return 0;
} 

【BZOJ 3534】[Sdoi2014] 重建

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3534
矩阵树定理基础:http://oi.cyo.ng/?p=717

解题报告

说明:以下使用变量名,均同上面的矩阵树定理基础

这个题目直接看矩阵树定理肯定是没有头绪的。
但如果我们看矩阵树定理推导过程中的这个式子:\(\partial = {\sum\limits_{G’isG’sSubgraph} {\det (M(G’))} ^2}\)
不难发现我们可以这样算贡献:\(\partial = {\sum\limits_{G’isG’sSubgraph} {\det (M(G’))} ^2} \cdot P\)
其中p为,在生成树中的点的出现概率和不在生成树中的点的不出现的概率
于是现在的问题就是如何把概率给搞进去。不难发现如果我们把每条边的概率乘到M(G’)中,刚好可以满足树边那部分的概率,但非树边那部分还满足不了
于是再来考虑非树边,凑一凑发现:我们把p/(1-p)乘进去,全局再乘以(1-p)就可以满足条件了
所以L=M(G)*M(G)^T*p/(1-p)

所以直接把矩阵树定理的邻接矩阵的0/1变成概率就可以了
或者,更直接的理解:矩阵树定理里面的邻接矩阵可以推广到连边的个数,那应该可以推广到实数域,这样的话就可以对应概率了?

Code

1A辣,撒花~ ★,°:.☆( ̄▽ ̄)/$:.°★

#include<iostream>
#include<cstdio>
#define abs(x) ((x)>0?(x):-(x))
using namespace std;

const int MAXN = 51;
const double EPS = 1e-8;

int n;
double G[MAXN][MAXN],rev=1;

inline double Gauss(){
	double ret = 1;
	for (int j=1,w=j;j<=n;w=++j) {
		for (int k=j+1;k<=n;k++) if (abs(G[j][k]) > abs(G[j][w])) w = k;
		if (w != j) {for (int i=1;i<=n;i++) swap(G[i][j],G[i][w]); ret *= -1;}
		for (int k=j+1;k<=n;k++) if (abs(G[j][k]) > EPS) {
			double t = G[j][k] / G[j][j];
			for (int i=1;i<=n;i++) G[i][k] -= G[i][j]*t;
		} ret *= G[j][j];
	} return ret;
}

int main(){
	scanf("%d",&n);
	for (int j=1;j<=n;j++) for (int i=1;i<=n;i++) scanf("%lf",&G[i][j]), rev *= i<j?1:1-G[i][j];
	for (int j=1;j<=n;j++) for (int i=1;i<=n;i++) G[i][j] = G[i][j]/(1-G[i][j])*-1;
	for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) if (i != j) G[i][i] -= G[j][i];
	n--; printf("%.10lf\n",Gauss()*rev);
	return 0;
}

【Codeforces 706E】Working routine

题目传送门:http://codeforces.com/problemset/problem/706/E
官方题解:http://codeforces.com/blog/entry/46510

这个题感觉出得很好啊!
没想到链表还能这么考!秒啊!
23781763871263

#include<iostream>
#include<cstdio>
using namespace std;

const int MAXN = 1000+9;

int d[MAXN*MAXN],r[MAXN*MAXN],mat[MAXN*MAXN],m,n,q,vout[MAXN][MAXN];

inline int read(){
	char c=getchar(); int ret = 0;
	while (c<'0'||c>'9') c=getchar();
	while (c<='9'&&c>='0') ret = ret*10+c-'0', c=getchar();
	return ret;
}

#define id(x, y) ((x)+1 + (y)*(n+2))

inline void change(int x1, int y1, int x2, int y2, int x, int y){
	int p1 = id(0,0), p2 = id(0,0); 
	for (int j=1;j<y1;j++) p1 = d[p1]; for (int i=1;i<=x1;i++) p1 = r[p1];
	for (int j=1;j<y2;j++) p2 = d[p2]; for (int i=1;i<=x2;i++) p2 = r[p2];
	for (int i=1;i<=x;i++) swap(d[p1], d[p2]), p1 = r[p1], p2 = r[p2];
	p1 = id(0,0), p2 = id(0,0); 
	for (int j=1;j<y1+y;j++) p1 = d[p1]; for (int i=1;i<=x1;i++) p1 = r[p1];
	for (int j=1;j<y2+y;j++) p2 = d[p2]; for (int i=1;i<=x2;i++) p2 = r[p2];
	for (int i=1;i<=x;i++) swap(d[p1], d[p2]), p1 = r[p1], p2 = r[p2];
	
	
	p1 = id(0,0), p2 = id(0,0); 
	for (int i=1;i<x1;i++) p1 = r[p1]; for (int j=1;j<=y1;j++) p1 = d[p1]; 
	for (int i=1;i<x2;i++) p2 = r[p2]; for (int j=1;j<=y2;j++) p2 = d[p2];
	for (int j=1;j<=y;j++) swap(r[p1], r[p2]), p1 = d[p1], p2 = d[p2];
	p1 = id(0,0), p2 = id(0,0); 
	for (int i=1;i<x1+x;i++) p1 = r[p1]; for (int j=1;j<=y1;j++) p1 = d[p1]; 
	for (int i=1;i<x2+x;i++) p2 = r[p2]; for (int j=1;j<=y2;j++) p2 = d[p2];
	for (int j=1;j<=y;j++) swap(r[p1], r[p2]), p1 = d[p1], p2 = d[p2];
}

inline void trans(){
	int head = id(0,0); 
	for (int j=1;j<=m;j++) {
		head = d[head];
		for (int i=1,w=r[head];i<=n;i++,w=r[w])
			vout[i][j] = mat[w];
	}
	for (int j=1;j<=m;j++) {for (int i=1;i<=n;i++) printf("%d ",vout[i][j]); printf("\n");}
}

int main(){
	m = read(); n = read(); q = read();
	for (int j=1;j<=m;j++) for (int i=1;i<=n;i++) mat[id(i,j)] = read();
	for (int j=0;j<=m+1;j++) for (int i=0;i<=n+1;i++) d[id(i,j)] = id(i,j+1), r[id(i,j)] = id(i+1,j);
	
	for (int k=1,x1,x2,y1,y2,lh,lw;k<=q;k++) {
		y1 = read(), x1 = read();
		y2 = read(), x2 = read();
		lh = read(), lw = read();
		change(x1,y1,x2,y2,lw,lh);
	} trans();
	return 0;
}

【Codeforces 698C】LRU

题目传送门:http://codeforces.com/problemset/problem/698/C

这个题目嘛,貌似第一眼都会想到状压?然后内存就爆炸了。
比赛的时候果断弃疗…….

不过,这道题真的出得很好欸!做了以后有一种神清气爽的感觉!o(* ̄▽ ̄*)ブ
具体来说,就是倒着来考虑:一个一个往里加,直到装满
详见官方题解:http://codeforces.com/blog/entry/46148

#include<iostream>
#include<cstdio>
using namespace std;

const int MAXN = 2000000;
const double EPS = 1e-12;

int n,k,cnt[MAXN],tot;
double f[MAXN],p[MAXN],vout[MAXN],mst[MAXN];

int main(){
	cin>>n>>k; f[0] = 1; mst[0] = 1;
	for (int i=1;i<=n;i++) {cin>>p[i]; if (p[i] > EPS) tot++;} tot = min(tot, k);
	for (int i=1,lim=1<<n;i<lim;i++) cnt[i] = __builtin_popcount(i);
	for (int i=0,lim=1<<n;i<lim;i++) if (cnt[i] < k) for (int j=1;j<=n;j++) 
		if ((i & (1<<(j-1))) == 0) f[i|(1<<(j-1))] += f[i]*p[j]/mst[i], mst[i|(1<<(j-1))] = mst[i] - p[j];
	for (int i=1,lim=1<<n;i<lim;i++) if (cnt[i] == tot && f[i] > EPS) 
		for (int j=1;j<=n;j++) if (i & (1<<(j-1))) vout[j] += f[i];
	for (int i=1;i<=n;i++) printf("%.16lf ",vout[i]);
	return 0;
}

【SOJ 1718】特工

题目传送门:http://oi.cyo.ng/wp-content/uploads/2016/08/216378216437812.png
题解传送门:http://oi.cyo.ng/wp-content/uploads/2016/08/123478756.png
OJ传送门:http://oi.cdshishi.net:8080/Problem/1718

考试的时候,看一眼数据范围
马上反应过来开是FFT,而且坚信是FFT
结果后来没想出来,考完试还被lcr给D了………
结果最后一看,真™是FFT!

这个题,看一眼马上想到高斯消元
于是考试的时候就写了O(n^3)的高斯消元

后来讲题的时候,说到其实就是一个矩阵求逆,我很赞同
于是就来补O(n^3)的矩阵求逆啦!

#include<iostream>
#include<cstdio>
#define LL long long
#define abs(x) ((x)<0?-(x):(x))
using namespace std;

const int MAXN = 3000;
const double EPS = 1e-8;

int n;
double mat[MAXN*2][MAXN],vout[MAXN],arr[MAXN];

inline LL read(){
	char c=getchar(); LL ret=0,f=1;
	while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0'){ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

#define lowbit(x) ((x)&-(x))
inline int bitcount(int w){
	int ret = 0;
	while (w) ret++, w -= lowbit(w);
	return ret;
}

int LCA(int a, int b){
	if (!b) return a;
	else return LCA(b,a%b);
}

inline void Get_Reverse_Matrix(){
	for (int i=1,w;i<=n;i++) {
		for (w=i;w<=n;w++) if (mat[i+n][w]) break;
		for (int j=1;j<=n*2;j++) swap(mat[j][w], mat[j][i]);
		if (abs(mat[i+n][i]) < EPS) continue;
		else for (int j=1;j<=n;j++) if (abs(mat[i+n][j]) > EPS && j != i) {
			double tmp = mat[i+n][j]/mat[i+n][i];
			for (int k=1;k<=n*2;k++) mat[k][j] -= mat[k][i]*tmp;
		}
		double tmp = mat[i+n][i];
		for (int j=1;j<=n*2;j++) mat[j][i] /= tmp;
	}
}

int main(){
	n = read(); 
	for (int i=1;i<=n;i++) arr[i] = read();
	for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) 
		mat[i+n][j] = (bitcount((i-1|j-1)^i-1)+1) % 2;
	for (int i=1;i<=n;i++) mat[i][i] = 1;
	Get_Reverse_Matrix();
	for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) 
		vout[i] += mat[i][j]*arr[j];
	for (int i=1;i<=n;i++) printf("%d ",(int)(vout[i]+EPS));
	return 0;
}

std是应用数据特点,把矩阵求逆给搞到了O(n^logn)
我就不写程序啦,反正SOJ代码可以看

—————————— UPD 2017.7.3 ——————————
仔细想一想,似乎按二进制分治也可以做?

【BZOJ 1414】[ZJOI2009] 对称的正方形

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1414

题号“1414”,还真的是把我做得“要死要死的”!做了一整天QAQ
Hash version:

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
 
const int MX = 0;
const int MAXN = 2000+9;
unsigned int SEEDX = 37;
unsigned int SEEDY = 137;
 
int n,m; LL vout;
unsigned int px[2000+9],py[2000+9],mat[MAXN][MAXN],f[5][MAXN][MAXN];
 
inline int read(){
    char c=getchar(); int ret=0;
    while (c<'0'||c>'9') c=getchar();
    while (c<='9'&&c>='0'){ret=ret*10+c-'0';c=getchar();}
    return ret;
}
 
inline void init(){
    m = read(); n = read(); vout = (unsigned int)n*m;
    for (int j=1;j<=m;j++) {
        for (int i=1;i<=n*2+1;i++) mat[i][j*2-1] = MX;
        for (int i=1;i<=n;i++) mat[i*2][j*2] = read(), mat[i*2-1][j*2] = MX;
        mat[n*2+1][j*2] = MX;
    } for (int i=1;i<=n*2+1;i++) mat[i][m*2+1] = MX;
    n = n*2+1; m = m*2+1;
}
 
inline void Hash_init(){
    px[0] = 1; for (int i=1;i<=2001;i++) px[i] = SEEDX*px[i-1];
    py[0] = 1; for (int i=1;i<=2001;i++) py[i] = SEEDY*py[i-1];
    for (int i=n,g=1;i;i--,g++) for (int j=1,h=1;j<=m;j++,h++) f[1][i][j] = px[g]*py[h]*mat[i][j] + f[1][i+1][j] + f[1][i][j-1] - f[1][i+1][j-1];
    for (int i=1,g=1;i<=n;i++,g++) for (int j=1,h=1;j<=m;j++,h++) f[2][i][j] = px[g]*py[h]*mat[i][j] + f[2][i-1][j] + f[2][i][j-1] - f[2][i-1][j-1];
    for (int i=1,g=1;i<=n;i++,g++) for (int j=m,h=1;j;j--,h++) f[3][i][j] = px[g]*py[h]*mat[i][j] + f[3][i-1][j] + f[3][i][j+1] - f[3][i-1][j+1];
    for (int i=n,g=1;i;i--,g++) for (int j=m,h=1;j;j--,h++) f[4][i][j] = px[g]*py[h]*mat[i][j] + f[4][i+1][j] + f[4][i][j+1] - f[4][i+1][j+1];
}
 
inline unsigned int Get_Hash(int t, int x1, int y1, int x2, int y2){
    if (t == 1) return (f[1][x1][y1] + f[1][x2+1][y2-1] - f[1][x2+1][y1] - f[1][x1][y2-1]) * px[2001-(n-x1+1)] * py[2001-y1];
    else if (t == 2) return (f[2][x1][y1] + f[2][x2-1][y2-1] - f[2][x2-1][y1] - f[2][x1][y2-1])* px[2001-x1] * py[2001-y1];
    else if (t == 3) return (f[3][x1][y1] + f[3][x2-1][y2+1] - f[3][x2-1][y1] - f[3][x1][y2+1]) * px[2001-x1] * py[2001-(m-y1+1)];
    else return (f[4][x1][y1] + f[4][x2+1][y2+1] - f[4][x2+1][y1] - f[4][x1][y2+1]) * px[2001-(n-x1+1)] * py[2001-(m-y1+1)];
}
 
inline bool judge(int x, int y, int len){
    unsigned int t1 = Get_Hash(1,x,y,x+len,y-len),t2 = Get_Hash(2,x,y,x-len,y-len);
    if (t1 != t2) return false;
    t1 = Get_Hash(3,x,y,x-len,y+len);
    if (t1 != t2) return false;
    t2 = Get_Hash(4,x,y,x+len,y+len);
    if (t1 != t2) return false;
    else return true;
}
 
inline void solve(){
    for (int j=1;j<=m;j++) for (int i=1;i<=n;i++) if ((i&1) + (j&1) != 1) {
        int l=2,r=min(min(i-1,j-1),min(n-i,m-j)),ans=0,mid;
        while (l <= r) {
            mid = l + r >> 1;
            if (judge(i,j,mid)) l = mid+1, ans = mid;
            else r = mid-1;
        }vout += ans/2;
    }
}
 
int main(){
    init(); Hash_init(); solve();
    printf("%lld\n",vout);
    return 0;
}  

Manacher version:

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
 
const int MAXN = 2000+9;
 
int mat[MAXN][MAXN],n,m,tmp[MAXN],ans[MAXN],que[MAXN],tot,pos[MAXN];
int res_x[MAXN][MAXN],res_y[MAXN][MAXN],sa_y[MAXN][MAXN],sa_x[MAXN][MAXN];
 
inline int read(){
    char c=getchar(); int ret=0;
    while (c<'0'||c>'9') c=getchar();
    while (c<='9'&&c>='0'){ret=ret*10+c-'0';c=getchar();}
    return ret;
}
 
inline void manacher(int lim){
    int MX=0; ans[0] = 0;
    for (int i=1;i<=lim;i++) {
        if (MX+ans[MX] > i) ans[i] = min(MX+ans[MX]-i, ans[MX*2-i]);
        else ans[i] = 0;
        while (tmp[i+ans[i]+1] == tmp[i-ans[i]-1]) ans[i]++;
        if (ans[i]+i > MX+ans[MX]) MX = i;
    }
}
 
inline void DP(int lim){
    tot = 0; pos[0] = 0; int sta = 0;
    for (int i=1;i<=lim;i++) {
        while (tot && ans[i] <= que[tot]) tot--; sta = min(sta, tot);
        que[++tot] = ans[i]; pos[tot] = i; int w = sta;
        while (w < tot && min(que[w],(i-pos[w-1])*2-1) <= min(que[w+1],(i-pos[w])*2-1)) w++;
        sta = w; tmp[i] = min(que[w],(i-pos[w-1])*2-1);
    } 
}

int main(){
    m = read(); n = read();
    for (int j=1;j<=m;j++) for (int i=1;i<=n;i++) mat[i*2][j*2] = read();
    n = n * 2 + 1; m = m * 2 + 1;
     
    for (int i=1;i<=n;i++) {
        for (int j=1;j<=m;j++) tmp[j] = mat[i][j]; tmp[0] = -1; tmp[m+1] = -2;
        manacher(m); 
        for (int j=1;j<=m;j++) sa_y[i][j] = ans[j];
    }
    for (int j=1;j<=m;j++) {
        for (int i=1;i<=n;i++) ans[i] = sa_y[i][j]*2+1;
        DP(n);
        for (int i=1;i<=n;i++) res_x[i][j] = tmp[i];
        for (int i=1;i*2<=n;i++) swap(ans[i],ans[n-i+1]);
        DP(n);
        for (int i=1;i<=n;i++) res_x[i][j] = min(res_x[i][j],tmp[n-i+1]);
    } 
         
    for (int j=1;j<=m;j++) {
        for (int i=1;i<=n;i++) tmp[i] = mat[i][j]; tmp[0] = -1; tmp[n+1] = -2;
        manacher(n); 
        for (int i=1;i<=n;i++) sa_x[i][j] = ans[i];
    }
    for (int i=1;i<=n;i++){
        for (int j=1;j<=m;j++) ans[j] = sa_x[i][j]*2+1;
        DP(m);
        for (int j=1;j<=m;j++) res_y[i][j] = tmp[j];
        for (int j=1;j*2<=m;j++) swap(ans[j],ans[m-j+1]);
        DP(m);
        for (int j=1;j<=m;j++) res_y[i][j] = min(res_y[i][j], tmp[m-j+1]);
    } 
     
    LL vout = (n/2)*(m/2);
    for (int j=1;j<=m;j++) for (int i=1;i<=n;i++) if ((i+j) % 2 == 0)
        vout += max(0,(min(res_x[i][j]-1,res_y[i][j]-1)/2)/2);
    printf("%lld\n",vout);
    return 0;
} 

【SPOJ 7001】VLATTICE

题目传送门:http://www.spoj.com/problems/VLATTICE/
离线版题目:http://paste.ubuntu.com/20300942/

仪仗队升级版!
然而还是不会QAQ 还是可耻地看了题解QAQ

#include<iostream>
#include<cstdio>
#include<bitset>
#define LL long long
using namespace std;

const int MAXN = 1000000+9;
const int MX = 1000000;

int pri[MAXN],tot,mu[MAXN];
bitset<MAXN> tag;

inline void Get_mu(){
	mu[1] = 1;
	for (int i=2;i<=MX;i++) {
		if (!tag[i]) pri[++tot] = i, mu[i] = -1;
		for (int j=1;j<=tot && i*pri[j]<=MX;j++){
			tag[i*pri[j]] = 1;
			if (i % pri[j]) mu[i*pri[j]] = -mu[i];
			else {mu[i*pri[j]]=0; break;}
		}
	}
	for (int i=1;i<=MX;i++) mu[i] += mu[i-1];
}

int main(){
	Get_mu(); int n,T,tmp; cin>>T;
	while (T--) {
		scanf("%d",&n); LL vout = 0;
		for (int i=1;i<=n;i=tmp+1){
			tmp = n/(n/i);
			vout += (LL)(mu[tmp]-mu[i-1])*(n/i)*(n/i);
		} vout = vout*3+3;
		for (int i=1;i<=n;i=tmp+1){
			tmp = n/(n/i);
			vout += (LL)(mu[tmp]-mu[i-1])*(n/i)*(n/i)*(n/i);
		}
		printf("%lld\n",vout); 
	}
	return 0;
} 

—– UPD 2016.9.21 —–
今天和同学们讨论这个题目的时候
发现,这题根本不需要莫比乌斯反演嘛 (╯‵□′)╯︵┻━┻
\(\sum\limits_{i = 1}^n {\sum\limits_{j = 1}^n {\sum\limits_{k = 1}^n {[\gcd (i,j,k) = 1] = } } } \sum\limits_{i = 1}^n {\sum\limits_{j = 1}^n {\sum\limits_{k = 1}^n {\sum\limits_{d|\gcd (i,j,k)} {\mu (d)} = \sum\limits_{d = 1}^n {\mu (d) \cdot {{\left\lfloor {\frac{n}{d}} \right\rfloor }^3}} } } } \)

【BZOJ 2705】[SDOI2012] Longge的问题

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2705
离线版题目:http://paste.ubuntu.com/20283268/

这题真的是蜜汁复杂度。
自己想的时候想到了,然而没敢写QAQ

首先是枚举gcd,然后搞phi或者mu
这个很显然,但只能过60%的数据

因为这个题是求gcd(i,n),所以gcd的取值只会有σ(n)个(有一个是定值),也就是n的因数的个数
然后用sqrt(n)来求每个因数的phi()

这样的话,时间复杂度上界是(sqrt(n))^2=O(n)的
这题n=10^9那不T到死QAQ
然而万能的vfk告诉我们,10^9内σ(n)最大为1000的样子,这样就不会T了QAQ
前排膜拜vfk:http://vfleaking.blog.163.com/blog/static/174807634201341913040467/
前排膜拜hht:http://techotaku.lofter.com/post/4856f0_634219b
ps:hht告诉我们,除数函数可以线筛,然而一脸懵逼QAQ

然而这题的还有一个trick,求phi()那里,还可以dfs算QAQ
复杂度更优!快4倍的样子
DFS-version:https://oi.abcdabcd987.com/eight-gcd-problems/(我就偷懒不写啦!)
original-version:

#include<iostream>
#include<cstdio>
#include<cmath>
#define LL long long
using namespace std;

inline LL Get_Phi(LL n){
	LL ret = n;
	for (int i=2,lim=ceil(sqrt(n));i<=lim;i++) if (n % i == 0) {
		ret = ret*(i-1)/i;
		while (n % i == 0) n /= i;
	}
	if (n > 1) ret = ret*(n-1)/n;
	return ret;
}

int main(){
	LL n,vout=0; scanf("%lld",&n);
	for (int i=1,lim=ceil(sqrt(n));i<=lim;i++) if (n%i == 0) {
		vout += (LL)i*Get_Phi(n/i);
		if (i*i < n) vout += (LL)(n/i)*Get_Phi(i);
	}
	printf("%lld\n",vout);
	return 0;
} 

—————————— UPD 2017.4.8 ——————————
找到这题的爸爸了:BZOJ 4802
也是求$\phi (n)$但$n \le 10^{18}$

【BZOJ 2005】[Noi2010] 能量采集 Advance

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2005
前传传送门:http://oi.cyo.ng/?p=477

之前%JCVB的没有成功。
今天又来%,基本上有思路了,真的是_(:з」∠)_

引理:\(\varphi (n) = \sum\limits_{d|n} {\frac{n}{d} \cdot \mu (d)}\)
证明:\(\varphi (n) = \sum\limits_{i = 1}^n {[\gcd (i,n) = = 1] = \sum\limits_{i = 1}^n {\sum\limits_{d|i} {\sum\limits_{d|n} {\mu (d) = \sum\limits_{d|n} {\mu (d) \cdot \sum\limits_{i = 1}^n {[\gcd (i,n) = = i] = } } } } } } \sum\limits_{d|n} {\mu (d) \cdot \frac{n}{d}}\)

我们之前的式子是:\(\sum\limits_{d = 1}^n {\sum\limits_{k = 1}^{\left\lfloor {\frac{n}{d}} \right\rfloor } {d \cdot \mu (k) \cdot \left\lfloor {\frac{n}{{d \cdot k}}} \right\rfloor } } \cdot \left\lfloor {\frac{m}{{d \cdot k}}} \right\rfloor\)
注意看好,我要变形啦!( •̀ ω •́ )y
不妨设:D=k*d,则有原式=\(\sum\limits_{D = 1}^n {\sum\limits_{d|D} {d \cdot \mu (\frac{D}{d}) \cdot } } \left\lfloor {\frac{n}{D}} \right\rfloor \cdot \left\lfloor {\frac{m}{D}} \right\rfloor \)
根据引理可以进一步简化为:\(\sum\limits_{D = 1}^n {\varphi ({\rm{D}})} \cdot \left\lfloor {\frac{n}{D}} \right\rfloor \cdot \left\lfloor {\frac{m}{D}} \right\rfloor \)
然后就可以线筛出欧拉函数后,\(\sqrt n \)分块就好,总时间复杂度:O(n)

#include<iostream>
#include<cstdio>
#include<bitset>
#define LL long long
using namespace std;

const int MAXN = 100000+9;

int n,m,pri[MAXN],tot,tmp;
LL phi[MAXN],vout;
bitset<MAXN> tag;

inline void Get_Phi(){
	phi[1] = 1;
	for (int i=2;i<=m;i++){
		if (!tag[i]) pri[++tot] = i, phi[i] = i-1;
		for (int j=1;j<=tot && pri[j]*i<=m;j++){
			tag[i*pri[j]] = 1;
			if (i % pri[j]) phi[i*pri[j]] = phi[i]*(pri[j]-1);
			else {phi[i*pri[j]] = phi[i]*pri[j]; break;}
		}
	}
	for (int i=1;i<=m;i++) phi[i] += phi[i-1];
}

int main(){
	scanf("%d%d",&n,&m);
	if (n > m) swap(n, m); Get_Phi();
	for (int i=1;i<=n;i=tmp+1){
		tmp = min(n/(n/i),m/(m/i));
		vout += (LL)(phi[tmp]-phi[i-1])*(n/i)*(m/i);
	}
	printf("%lld\n",vout*2-(LL)n*m);
	return 0;
} 

哦,对了,这类直线上点的个数的问题,有一个神奇的结论:
(x,y)这个点到原点的连线上整点的个数为gcd(x,y)
为什么会有这个结论呢?
因为这个相当于把线段分成了完全相同的gcd(x,y)段
或者可以这样理解:
离原点最近的一个肯定是(x/gcd(x,y),y/gcd(x,y))
之后的每一整点都是他的整数倍,所以总共有gcd(x,y)个

—————————— UPD 2017.2.1 ——————————
今天又看了看这个玩意儿,为什么要$\sqrt n$分块QwQ
暴力不也这复杂度吗……

而且完全不知道$\sum\limits_{d|n} {\frac{n}{d} \cdot \mu (d)}$是$\varphi (n)$也完全可以做啊!
根据狄利克雷卷积,可以知道这货是积性函数,然后再推一推发现可以线筛,然后直接做就好啊!

—————————— UPD 2017.7.3 ——————————
我发现自己之前数学好强
现在看这个题已经不会了qwq

【NOI六连测】[D6T1] 互质

题目传送门:http://pan.baidu.com/s/1eR1sqXk
离线版题目:http://paste.ubuntu.com/18699825/
数据传送门:http://pan.baidu.com/s/1o8pgYZg

这个题目,暴力的状压很容易想到,然而质因数太多,数组开不下 QAQ
于是考试的时候,码个暴力就闪人了。一看题解,真的是好妙啊!o( ̄▽ ̄)ブ

我们只拆分33以内的质因数,这里只有12个,可以承受。
这么干,还有一个好处:对于一个1k以内的数,超过33的质因数只可能有一个
也就是说,我们对于任意一个数,分两种情况讨论:
1)只有33以内的质因数:这样的话,直接暴力转移即可
2)有33以上的质因数:将拥有相同的、大于33的质因数的数存成一组,像分组背包一样一起转移
因为大于33的质因数只可能有一个,而相同的存在了一起,所以只要保证小于34的质因数不重合即可

此题真的是妙啊!

#include<iostream>
#include<cstdio>
using namespace std;

const int MAXN = 5000;
const int LIM = 4500;
const int N = 12;

int sed[]={0,2,3,5,7,11,13,17,19,23,29,31,33};
int n,que[MAXN][MAXN],cnt[MAXN];
int t1[MAXN],t2[MAXN],*f,*g;

inline int read(){
	char c=getchar(); int buf=0,f=1;
	while (c<'0'||c>'9'){if(c=='-')f-1;c=getchar();}
	while (c<='9'&&c>='0'){buf=buf*10+c-'0';c=getchar();}
	return buf*f;
}

int main(){
	freopen("prime.in","r",stdin);
	freopen("prime.out","w",stdout);
	n = read();
	f=t1; g=t2;
	
	for (int i=1,a;i<=n;i++){
		a = read();
		int tmp = a, buf = 0;
		for (int i=1;i<=N;i++) if (!(tmp%sed[i])) {
			while (!(tmp%sed[i])) tmp /= sed[i];
			buf |= 1<<(i-1);
		} 
		if (tmp != 1) que[tmp][++cnt[tmp]] = buf;
		else for (int i=1;i<=LIM;i++) if (!(i&buf)) 
			f[i|buf] = max(f[i|buf], f[i]+1);
	}
	
	for (int i=1;i<=1000;i++){
		for (int k=1;k<=LIM;k++) g[k] = f[k];
		for (int j=1;j<=cnt[i];j++)	for (int k=1;k<=LIM;k++)
			if (!(k&que[i][j])) g[k|que[i][j]] = max(g[k|que[i][j]], f[k]+1);		
		swap(g,f);
	}
	
	int vout = 1;
	for (int i=1;i<=LIM;i++)
		vout = max(vout, f[i]);
	printf("%d\n",vout);
	
	return 0;
}	

—————————— UPD 2017.2.1 ——————————
总感觉这个题DLX可以暴力过的样子
有没有同学有空写写啊!

【NOI六连测】[D4T2] 最短路

题目传送门:http://pan.baidu.com/s/1eRRG6Wy
离线版题目:http://paste.ubuntu.com/18687179/
数据传送门:http://pan.baidu.com/s/1qYtM8f6
数据生成器:http://paste.ubuntu.com/18687249/

唉,以前在CF上,看过这道题,拿到后,一眼就记得跟线段树有关,然而还是没有做出来QAQ
说一说怎么做:
如果边权很小,那么我们直接跑最短路就好,如果边权再大一点,那我们就写高精度
然而这题有2^100000,这个大概有10000那么多位(DEC),高精度会T,而且空间也开不下
所以只能搞神奇的技巧。
我们那函数式线段树来模拟二进制数组。那么单次修改就是log(n)的时间和空间复杂度
那么,比较大小怎么搞?搞一搞Hash,然后找到第一位不同的地方比较大小,仍然是log(n)
那么怎么进位?单点赋一,区间赋零即可,也为log(n)
然后就是码代码的事情了,我代码只写了一个小时,然而调试了3小时QAQ

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define LL long long
using namespace std;

const int MAXN = 200000+9;
const int MOD = 1000000000+7;
const int HASH = 9999971;
const int SEED = 137;
const int INF = 100000000;

int n,m,T,to[MAXN],head[MAXN],nxt[MAXN],cost[MAXN];
int s,t,done[MAXN],tpw[MAXN];

inline int read(){
	char c=getchar(); int buf=0,f=1;
	while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0'){buf=buf*10+c-'0';c=getchar();}
	return buf*f;
}

namespace Chair_Tree{
	#define CT Chair_Tree
	#define N 10000000
	#define MX 200000
	int root[MAXN],hash[N],val[N],ch[N][2],MN[N];
	int ans_tmp,cnt;
	
	inline bool cmp(int w1, int w2){
		int l=1,r=MX,mid;
		while (l < r){
			mid = (l+r+1)/2;
			if (hash[ch[w1][1]] != hash[ch[w2][1]] || val[ch[w1][1]] != val[ch[w2][1]]) 
			w1 = ch[w1][1], w2 = ch[w2][1], l = mid;
			else w1 = ch[w1][0], w2 = ch[w2][0], r = mid-1; 
		}	
		return val[w1] > val[w2];
	}
	
	void find(int w, int l, int r, int pos){
		if (!w) ans_tmp = min(ans_tmp, l);
		else if (l >= pos) ans_tmp = min(ans_tmp, MN[w]);
		else if (r >= pos && l < r) {
			int mid = (l+r+1) / 2;
			if (pos < mid) find(ch[w][0], l, mid-1, pos);
			find(ch[w][1], mid, r, pos);
		}
	}inline int find(int w, int pos){ans_tmp = INF;find(w, 1, MX, pos);return ans_tmp;}	
	
	inline void maintain(int w, int l, int r){
		int len = (l+r+1)/2-l; MN[w] = INF;
		hash[w] = (LL)((LL)hash[ch[w][0]]+(LL)SEED*hash[ch[w][1]])*SEED % HASH;
		val[w] = (LL)((LL)val[ch[w][0]] + (LL)val[ch[w][1]]*tpw[len]) % MOD;
		if (ch[w][0]) MN[w] = min(MN[w], MN[ch[w][0]]);
		else MN[w] = min(MN[w], l);
		if (ch[w][1]) MN[w] = min(MN[w], MN[ch[w][1]]);
		else MN[w] = min(MN[w], (l+1+r)/2);
		if (l == r && !val[w]) MN[w] = min(MN[w], l);
	}
	
	void modify(int pre, int &w, int l, int r, int p){
		w = ++cnt; ch[w][0] = ch[pre][0]; ch[w][1] = ch[pre][1];
		if (l == r) hash[w] = 1, val[w] = 1, MN[w] = INF;
		else {
			int mid = (l+r+1) / 2;
			if (p < mid) modify(ch[pre][0], ch[w][0], l, mid-1, p);
			else modify(ch[pre][1], ch[w][1], mid, r, p);
			maintain(w,l,r);
		}
	}
	
	void clear(int pre, int &w, int l, int r, int L, int R){
		int mid = (l+r+1) / 2;
		if (L <= l && r <= R) w = 0;
		else {
			w = ++cnt; ch[w][0] = ch[pre][0]; ch[w][1] = ch[pre][1];
			if (L < mid) clear(ch[pre][0], ch[w][0], l, mid-1, L, R);
			if (R >= mid) clear(ch[pre][1], ch[w][1], mid, r, L, R);
			maintain(w,l,r);
		}  
	}
	
	inline int Add(int rt, int pos){
		int p1 = max(find(rt, pos),pos), ret;
		modify(rt, ret, 1, MX, p1);
		if (p1 > pos) clear(ret, ret, 1, MX, pos ,p1-1);
		return ret;
	}
	
	inline void output(int rt){
		printf("%d\n",val[rt]%MOD);
	}
	
	inline void print_path(){
		int w = t; cout<<t<<endl;
		while (w != s){
			for (int i=head[w];i;i=nxt[i]){
				int tmp = Add(root[to[i]], cost[i]);
				if (cmp(tmp,root[w])^cmp(root[w],tmp)==0) 
					w = to[i], cout<<w<<' '<<val[root[w]]<<endl;
			}
		}
		cout<<s;
	}
	
	void build(int &w, int l, int r){
		w = ++cnt; MN[w] = INF;
		if (l == r) hash[w] = val[w] = 1;
		else {
			int mid = (l+r+1)/2;
			build(ch[w][0], l, mid-1);
			build(ch[w][1], mid, r);
			maintain(w,l,r);
		}
	}inline int build_Tree(){int ret; build(ret,1,MX); return ret;}
};

struct DATA{
	int p,rt; DATA(){}
	DATA(int P, int RT):p(P),rt(RT){}
	bool operator < (const DATA &B) const {
		return CT::cmp(rt, B.rt);
	}
};
priority_queue<DATA> Q;

inline void AddEdge(int a, int b, int c){
	to[++T] = b; nxt[T] = head[a]; head[a] = T; cost[T] = c;
	to[++T] = a; nxt[T] = head[b]; head[b] = T; cost[T] = c;
}

inline void Dijkstra(){
	int tmp = CT::build_Tree();
	for (int i=1;i<=n;i++) CT::root[i] = tmp;
	CT::root[s] = 0;
	Q.push(DATA(s,CT::root[s]));
	
	while (!Q.empty()){
		DATA w = Q.top(); Q.pop();
		if (done[w.p]) continue;
		else if (w.p == t) {
			CT::output(w.rt);return;		
		} else {
			done[w.p] = 1;
			for (int i=head[w.p];i;i=nxt[i]){
				if (done[to[i]]) continue;
				else {
					tmp = CT::Add(w.rt,cost[i]);
					if (CT::cmp(CT::root[to[i]], tmp))
						CT::root[to[i]] = tmp, 
						Q.push(DATA(to[i], tmp));		
				}
			}	
		}
	}
	printf("-1\n");
}

int main(){
	freopen("shortest.in","r",stdin);
	freopen("shortest.out","w",stdout);
	n = read(); m = read();
	for (int i=1,a,b,c;i<=m;i++)
		a=read(), b=read(), c=read(),
		AddEdge(a, b, c+1);
	s = read(); t = read(); tpw[0] = 1; 
	for(int i=1;i<=150000;i++)
		tpw[i] = (LL)tpw[i-1]*2%MOD;
 	
	Dijkstra();
	
	return 0;
}