@ljt12138
2017-04-09T15:43:31.000000Z
字数 16496
阅读 1006
神题...
首先第三维可以忽略。
#include <bits/stdc++.h>using namespace std;struct p {double x, y;p operator - (const p &a){ return (p) {x-a.x, y-a.y}; }double operator * (const p &a){ return x*a.y-y*a.x; }} mat[505], tar[505];int m, n;double tmp, eps = 1e-10;double dot(const p &a, const p &b){return a.x*b.x + a.y*b.y;}int sgn(double x){if (abs(x) <= eps) return 0;return x > 0 ? 1 : -1;}bool on_side(int i, int j){for (int k = 1; k <= n; k++)if ((tar[k]-mat[i])*(mat[j]-mat[i]) > eps ||(abs((tar[k]-mat[i])*(mat[j]-mat[i])) <= eps && dot(mat[i]-tar[k], mat[j]-tar[k]) > eps)) return 0;// cout << i << " " << j << " " << gt << " " << ls << " " << endl;return 1;}int g[505][505];int main(){scanf("%d%d", &m, &n);for (int i = 1; i <= m; i++)scanf("%lf%lf%lf", &mat[i].x, &mat[i].y, &tmp);for (int i = 1; i <= n; i++)scanf("%lf%lf%lf", &tar[i].x, &tar[i].y, &tmp);memset(g, 127/3, sizeof g);for (int i = 1; i <= m; i++)for (int j = 1; j <= m; j++)if (on_side(i, j))g[i][j] = 1;for (int k = 1; k <= m; k++)for (int i = 1; i <= m; i++)for (int j = 1; j <= m; j++)g[i][j] = min(g[i][j], g[i][k]+g[k][j]);int ans = 233333333;for (int i = 1; i <= m; i++) ans = min(ans, g[i][i]);if (ans >= 23333333) cout << -1 << endl;else cout << ans << endl;return 0;}
第一道数位dp233...果然神烦,调了一个小时左右吧(主要还是递推关系找错)
就是用
small trick:用把逻辑判断深入
偷了个懒用了ctrl+c ctrl+v
另外吐槽黄学长代码随手hack...
#include <bits/stdc++.h>using namespace std;long long dp[20][10][4]; // 前i位,最后一位是j,能不能随便填/前导0long long A, B;int SA[20], SB[20];int lA = 0, lB = 0;int main(){scanf("%lld%lld", &A, &B); A--;for (long long i = A; i; i /= 10) SA[++lA] = i%10;for (long long i = B; i; i /= 10) SB[++lB] = i%10;/* calc ans ls B*/for (int i = 1; i < SB[lB]; i++) dp[lB][i][1] = 1;dp[lB][SB[lB]][0] = 1, dp[lB][0][2] = 1;for (int i = lB-1; i >= 1; i--) {for (int j = 0; j < 10; j++) {for (int k = 0; k <= 2; k++)for (int l = 0; l < 10; l++)for (int p = 0; p <= 2; p++)dp[i][j][k] += dp[i+1][l][p]*(p==2||abs(j-l) >= 2)*((k==0&&p==0&&j==SB[i])||(k==1&&(p==1||(p==2&&j)||(p==0&&j<SB[i])))||(k==2&&j==0&&p==2));}}long long ans1 = 0, ans2 = 0;for (int i = 0; i < 10; i++) ans1 += dp[1][i][0]+dp[1][i][1];//+dp[1][i][2];memset(dp, 0, sizeof dp);/* now ls A*/for (int i = 1; i < SA[lA]; i++) dp[lA][i][1] = 1;dp[lA][SA[lA]][0] = 1, dp[lA][0][2] = 1;for (int i = lA-1; i >= 1; i--) {for (int j = 0; j < 10; j++) {for (int k = 0; k <= 2; k++)for (int l = 0; l < 10; l++)for (int p = 0; p <= 2; p++)dp[i][j][k] += dp[i+1][l][p]*(p==2||abs(j-l) >= 2)*((k==0&&p==0&&j==SA[i])||(k==1&&(p==1||(p==2&&j)||(p==0&&j<SA[i])))||(k==2&&j==0&&p==2));}}for (int i = 0; i < 10; i++) ans2 += dp[1][i][0]+dp[1][i][1];cout << ans1-ans2 << endl;return 0;}
显然(表示蒟蒻看不出哪里显然)四边形的两个点是凸包上一对对踵点,所以旋转卡壳找对踵点然后枚举另外左右两个点就好了。
#include <bits/stdc++.h>using namespace std;struct vec {double x, y;double len_pow() const{ return x*x+y*y; }friend vec operator - (const vec &a, const vec &b){ return (vec) {a.x-b.x, a.y-b.y}; }friend double operator * (const vec &a, const vec &b){ return a.x*b.y-a.y*b.x; }friend bool operator == (const vec &a, const vec &b){ return b.x == a.x && b.y == a.y; }} pts[2005];int n;vec lft;double eps = 1e-7;int sgn(double a){ return abs(a) <= eps ? 0 : (a>eps?1:-1); }bool cmp(const vec &a, const vec &b){if (a == lft || b == lft) return a == lft;return sgn((b-lft)*(a-lft)) == 0 ? a.len_pow() < b.len_pow() : sgn((b-lft)*(a-lft)) > 0;}stack<vec> stk;vec pl[2005];int tp = 0;void graham(){lft.x = 1234567;for (int i = 1; i <= n; i++)if (pts[i].x < lft.x) lft = pts[i];sort(pts+1, pts+n+1, cmp);for (int i = 1; i <= n; i++) {if (stk.empty()) stk.push(pts[i]);else {while (stk.size() >= 2) {vec tp = stk.top(); stk.pop();vec t2 = stk.top(); stk.pop();if ((t2-tp)*(pts[i]-tp) > 0) {stk.push(t2), stk.push(tp); break;}else stk.push(t2);}stk.push(pts[i]);}}stk.push(lft);tp = stk.size();for (int i = tp-1; i >= 0; i--)pl[i] = stk.top(), stk.pop();double ans = 0;for (int i = 0, j = 1; i < tp; i++) {double l = 0, r = 0;if (j == i) j = (j+1)%tp;while((pl[(j+1)%tp]-pl[i]).len_pow() >= (pl[j]-pl[i]).len_pow())j = (j+1)%tp;for (int k = 0; k < tp; k++)if (sgn((pl[k]-pl[i])*(pl[j]-pl[i])) <= 0) l = max(l, abs((pl[k]-pl[i])*(pl[j]-pl[i]))/2);else r = max(r, abs((pl[k]-pl[i])*(pl[j]-pl[i]))/2);ans = max(ans, l+r);}printf("%.3f", ans);}int main(){scanf("%d", &n);for (int i = 1; i <= n; i++)scanf("%lf %lf", &pts[i].x, &pts[i].y);graham();return 0;}
输入
然而后缀数组写一遍就要看一遍模板...
一会再练习一下二分+hash大法...感觉做这个题应该很轻松
#include <bits/stdc++.h>using namespace std;const int MAXN = 200005;struct SA {struct p {int k[2], id;p(){}p(int i, int a, int b) { id = i, k[0] = a, k[1] = b; }} RE[MAXN], RT[MAXN];int C[MAXN], SA[MAXN], height[MAXN], rank[MAXN], str[MAXN], n;void radix_sort(){for (int y = 1; y >= 0; y--) {memset(C, 0, sizeof C);for (int i = 1; i <= n; i++) C[RE[i].k[y]]++;for (int i = 1; i <= 200000; i++) C[i] += C[i-1];for (int i = n; i >= 1; i--) RT[C[RE[i].k[y]]--] = RE[i];for (int i = 1; i <= n; i++) RE[i] = RT[i];}for (int i = 1; i <= n; i++) {rank[RE[i].id] = rank[RE[i-1].id];if (RE[i].k[0] != RE[i-1].k[0] || RE[i].k[1] != RE[i-1].k[1])rank[RE[i].id]++;}}void calc_sa(){for (int i = 1; i <= n; i++) RE[i] = p(i, str[i], 0);radix_sort();for (int k = 1; k < n; k <<= 1) {for (int i = 1; i <= n; i++) RE[i] = p(i, rank[i], i+k<=n?rank[i+k]:0);radix_sort();}for (int i = 1; i <= n; i++)SA[rank[i]] = i;}} SA;char str[MAXN];int main(){scanf("%s", str+1);int n = strlen(str+1); SA.n = n*2;for (int i = 1; i <= n; i++) SA.str[i] = SA.str[i+n] = str[i];SA.calc_sa();for (int i = 1; i <= n*2; i++) {if (SA.SA[i] <= n) {printf("%c", SA.str[SA.SA[i]+n-1]);}}return 0;}
这就是所谓环套树上dp吗?
求环套树上最大独立集。
由于每棵树至多一个环,只需要从
#include <bits/stdc++.h>using namespace std;const int MAXN = 1000005;int x[MAXN];int n;struct node {int to, next;} edge[MAXN*2];int head[MAXN], top = 0;void push(int i, int j){ ++top, edge[top] = (node) { j, head[i]}, head[i] = top; }struct tree {struct node {int to, next;} edge[MAXN];int head[MAXN], top, vis[MAXN], rd[MAXN], v[MAXN], vt;long long dp[MAXN], dpban[MAXN];void clear(){ top = vt = 0; memset(head, 0, sizeof head); memset(vis, -1, sizeof vis);memset(rd, 0, sizeof rd); memset(v, 0, sizeof v); }void push(int i, int j){//cout << i<<"-->" << j << endl;++top, edge[top] = (node) { j, head[i]}, head[i] = top, rd[j]++;if (vis[i] == -1) v[++vt] = i, vis[i] = 0;if (vis[j] == -1) v[++vt] = j, vis[j] = 0;}long long dfs(int nd){// cout << "dfs " << nd << endl;if (dp[nd] != -1) return dp[nd];if (vis[nd]) return dfs_ban(nd);// --- choose nd ---dp[nd] = x[nd];for (int i = head[nd]; i; i = edge[i].next)dp[nd] += dfs_ban(edge[i].to);// --- not choose nd ---long long ans = 0;for (int i = head[nd]; i; i = edge[i].next)ans += dfs(edge[i].to);return dp[nd] = max(dp[nd], ans);}long long dfs_ban(int nd){//cout << "dfs_ban " << nd << endl;if (dpban[nd] != -1) return dpban[nd];dpban[nd] = 0;for (int i = head[nd]; i; i = edge[i].next)dpban[nd] += dfs(edge[i].to);return dpban[nd];}long long result(){memset(dp, -1, sizeof dp), memset(dpban, -1, sizeof dpban);int rt = 0;for (int i = 1; i <= vt; i++)if (rd[v[i]] == 0) {rt = v[i];break;}return dfs(rt);}} T;int vis[MAXN];int u, v; // 拆环点void dfs(int i, int f = 0){vis[i] = 1;for (int k = head[i]; k; k = edge[k].next) {int to = edge[k].to;if (f == to);else if (!vis[to]) T.push(i, to), dfs(to, i);else u = i, v = to;}}long long work(int i){T.clear();dfs(i);// cout << u << " " << v << endl;long long ans = 0;T.vis[u] = 0, T.vis[v] = 1;ans = max(ans, T.result());T.vis[v] = 0, T.vis[u] = 1;ans = max(ans, T.result());return ans;}int main(){scanf("%d", &n);for (int i = 1; i <= n; i++) {int tar;scanf("%d%d", &x[i], &tar);push(i, tar), push(tar, i);}memset(vis, 0, sizeof vis);long long ans = 0;for (int i = 1; i <= n; i++)if (!vis[i])ans += work(i);cout << ans << endl;return 0;}
set水过...
鉴于那个年代不能stl,再写一个splay..
其实那个年代也没有splay
set:
#include <bits/stdc++.h>using namespace std;set<int> table;int n;int main(){scanf("%d", &n);int cnt = 0;for (int i = 1; i <= n; i++) {int a; if (scanf("%d", &a)==EOF) a = 0;set<int>::iterator k = table.lower_bound(a), t = k;if (k != table.begin()) k--;int ans = INT_MAX;if (i == 1) ans = a;else {if (t != table.end()) ans = min(ans, abs(*t-a));//, printf("--%d--\n", *t);if (k != table.end()) ans = min(ans, abs(*k-a));//, printf("--%d--\n", *k);}cnt += ans;table.insert(a);}cout << cnt << endl;return 0;}
splay:
#include <bits/stdc++.h>using namespace std;const int MAXN = 40005;int dat[MAXN], chl[MAXN][2], fa[MAXN], top = 0, root = 0;int new_node(int ke, int f){ return dat[++top] = ke, fa[top] = f, top; }void zig(int nd){int p = fa[nd], g = fa[p], tp = chl[p][0] != nd, tg = chl[g][0] != p;int son = chl[nd][tp^1];if (son) fa[son] = p; fa[p] = nd, fa[nd] = g;if (g) chl[g][tg] = nd; else root = nd;chl[nd][tp^1] = p, chl[p][tp] = son;}void splay(int nd){while (fa[nd]) {int p = fa[nd], g = fa[p], tp = chl[p][0] != nd, tg = chl[g][0] != p;if (!g) {zig(nd); break; }if (tp == tg) zig(p), zig(nd);else zig(nd), zig(nd);}}void push_ele(int ke){if (!root) { root = new_node(ke, 0); return; }int nd = root, p = fa[nd];while (nd) p = nd, nd = chl[nd][dat[nd]<=ke];chl[p][dat[p] <= ke] = new_node(ke, p);splay(chl[p][dat[p]<=ke]);}int ans_of(int num){if (!root) return num;int ans = INT_MAX;int nd = root;while (nd) {ans = min(ans, abs(dat[nd]-num));nd = chl[nd][dat[nd]<=num];}return ans;}int main(){int n, a;memset(chl, 0, sizeof chl);memset(fa, 0, sizeof fa);scanf("%d", &n);int sum = 0;for (int i = 1; i <= n; i++) {if (scanf("%d", &a) == EOF) a = 0;sum += ans_of(a), push_ele(a);}cout << sum << endl;return 0;}
手玩一会发现方法是唯一的。
凡汉诺塔就考虑最大盘,枚举最大盘两种行进模式,然后递归判断是否可行。那个优先级限制事实上只作用在一个盘子的时候。
最后带个记忆化即可。
#include <bits/stdc++.h>using namespace std;long long dp[35][4][4];pair<int,int> pr[7];int n;long long dfs(int n, int s, int t){if (dp[n][s][t] != -1) return dp[n][s][t];if (n == 1) {for (int i = 1; i <= 6; i++) {if (pr[i].first == s && pr[i].second != t) {dp[n][s][t] = -1234567; break;}if (pr[i].first == s && pr[i].second == t) {dp[n][s][t] = 1; break;}}} else {if (dfs(n-1, s, t) > 0 && dfs(n-1, t, s) > 0) dp[n][s][t] = 2+dfs(n-1, s, t)*2+dfs(n-1, t, s);else if (dfs(n-1, s, 6-s-t) > 0 && dfs(n-1, 6-s-t, t) > 0) dp[n][s][t] = dfs(n-1, s, 6-s-t) + dfs(n-1, 6-s-t, t) + 1;else dp[n][s][t] = -1234567;}// printf("%d,%d,%d ==> %lld\n", n, s, t, dp[n][s][t]);return dp[n][s][t];}int main(){memset(dp, -1, sizeof dp);scanf("%d", &n);for (int i = 1; i <= 6; i++) {char s[2];scanf("%s", s);pr[i] = make_pair(s[0]-'A'+1, s[1]-'A'+1);}if (dfs(n, 1, 3)>0) cout << dfs(n, 1, 3) << endl;else cout << dfs(n, 1, 2) << endl;return 0;}
和NIM很像啊,然而找不到太多思路,然后就用通用策略打表找规律,可以发现很强的结论:只需要特判非0即1的局面,剩下的和普通NIM一样(SG&XOR)。
#include <cstdio>using namespace std;int main(){int T;scanf("%d", &T);while (T--) {int n; scanf("%d", &n);int ans = 0, cnt = 0, c2 = 0;for (int i = 1; i <= n; i++) {int a; scanf("%d", &a);ans ^= a;if (a == 1) cnt++;if (a == 0) c2++;}if (cnt+c2 == n) {if (cnt&1) puts("Brother");else puts("John");} else {if (ans == 0) puts("Brother");else puts("John");}}return 0;}
题不是今天写的,但还是mark一下。
分裂之后两个石子就独立了,所以:
n很小,暴力算就可以了。
至于计数题,仍然大暴力,枚举分裂位置求新局面的sg异或和即可。
设原局面异或和为
#include <bits/stdc++.h>using namespace std;int a[30], sg[30];int n;int basket[100];int main(){int t; scanf("%d", &t);while (t--) {scanf("%d", &n);for (int i = 1; i <= n; i++)scanf("%d", &a[i]);sg[n] = 0;for (int i = n-1; i >= 1; i--) {memset(basket, 0, sizeof basket);for (int j = i+1; j <= n; j++)for (int k = j; k <= n; k++)basket[sg[j]^sg[k]] = 1;for (int j = 0; ; j++) if (basket[j] == 0) { sg[i] = j; break; }}int ans = 0;for (int i = 1; i <= n; i++)if (a[i]&1)ans ^= sg[i];if (ans == 0) {printf("-1 -1 -1\n0\n");} else {int a1, b, c, cnt = 0; a1 = b = c = -1;for (int i = 1; i <= n; i++)for (int j = i+1; j <= n; j++)for (int k = j; k <= n; k++)if (((ans^sg[i]^sg[j]^sg[k]) == 0) ){if (a1+b+c < 0) a1 = i, b = j, c = k;cnt++;}printf("%d %d %d\n%d\n", a1-1, b-1, c-1, cnt);}}return 0;}
神题......磕了一上午无果,后来看了大爷题解。
首先发现
然后就用奇奇怪怪的方法搞了一个每个位置都仅出现在一个数的线性基出来,然后贪心...
#include <bits/stdc++.h>using namespace std;long long bas[70];int n, kp, k; long long L, R;long long a[1010], upp;int v[70];long long query(long long up){if (up == -1) return -1;long long now = 0, ans = 0;for (int i = 1; i <= k; i++) {if ((now|bas[i]) <= up)now |= bas[i], ans += (1ll<<(k-i));}return ans;}int main(){memset(v, 0, sizeof v);scanf("%d%d%lld%lld", &n, &kp, &L, &R);for (int i = 1; i <= n; i++)scanf("%lld", &a[i]);upp = (1ll<<kp)-1;for (int j = kp-1; j >= 0; j--)if (!v[j]) {long long now = upp;for (int i = 1; i <= n; i++)if (!(a[i]&(1ll<<j)))now &= ~a[i]&upp;elsenow &= a[i];bas[++k] = now;for (int i = 0; i <= j; i++)if (now&(1ll<<i))v[i] = 1;}cout << query(R)-query(L-1) << endl;return 0;}
上午那个线性基太伤人...于是下午打算复习tarjan。
其实是重新学...
以前很喜欢做缩点不过只会ko..算法,学了tarjan感觉打开了新世界大门。才知道那个stack存的是根到这个点的路径...
貌似有大爷用点双做?
首先用tarjan算出割点集
特判没有割点要建两个。
初始化坑人...
#include <bits/stdc++.h>using namespace std;const int MAXN = 1050;int m;int arr[MAXN], tp = 0;struct graph {struct node {int to, next;} edge[MAXN];int head[MAXN], n, top, low[MAXN], dfn[MAXN], vis[MAXN], deg[MAXN], dft;int cut[MAXN], gp[MAXN], gp_top, lk[MAXN][MAXN], cut_cnt;void clear(){ memset(arr, 0, sizeof arr); memset(head,0,sizeof head); memset(vis, 0, sizeof vis);memset(cut, 0, sizeof cut); n = top = dft = gp_top = cut_cnt = 0;memset(deg, 0, sizeof deg); memset(gp, 0, sizeof gp);memset(dfn, 0, sizeof dfn); memset(lk, 0, sizeof lk); }void push(int i, int j){ ++top, edge[top] = (node) {j, head[i]}, head[i] = top; n = max(n, max(i,j)); }void tarjan(int nd, int f);void work(int);void dfs(int nd, int &siz, int &lkt);} g;void graph::tarjan(int nd, int f){vis[nd] = 1, low[nd] = dfn[nd] = ++dft;for (int i = head[nd]; i; i = edge[i].next) {int to = edge[i].to;if (!vis[to]) tarjan(to, nd), low[nd] = min(low[nd], low[to]), deg[nd]++;else if (to != f) low[nd] = min(low[nd], dfn[to]);if (dfn[nd] <= low[to]) {if (!f) cut[nd] = deg[nd]>=2; else cut[nd] = 1; cut_cnt += cut[nd];}}}void graph::dfs(int nd, int &siz, int &lkt){gp[nd] = gp_top, siz++;// cerr << "Group " << gp_top << " ---> " << nd << endl;for (int i = head[nd]; i; i = edge[i].next) {int to = edge[i].to;if (!gp[to] && !cut[to]) dfs(to, siz, lkt);else if (cut[to] && lk[gp_top][to] == 0) lk[gp_top][to] = 1, lkt++;}}void graph::work(int c){for (int i = 1; i <= n; i++)if (!dfn[i])tarjan(i, 0);if (cut_cnt == 0) {printf("Case %d: %d %lld\n", c, 2, (long long)n*(n-1)/2);return;}for (int i = 1; i <= n; i++) {if (!cut[i] && !gp[i]) {int siz = 0, lkt = 0;gp_top++, dfs(i, siz, lkt);if (lkt == 1) arr[++tp] = siz;}}long long ans = 1;for (int i = 1; i <= tp; i++) ans *= arr[i];printf("Case %d: %d %lld\n", c, tp, ans);}int main(){int cas = 0;while (scanf("%d", &m)) {g.clear(); tp = 0;if (m == 0) break;++cas;for (int i = 1; i <= m; i++) {int u, v; scanf("%d %d", &u, &v);g.push(u, v), g.push(v, u);}g.work(cas);}return 0;}
做过好几遍了...
练模板首选2333
#include <bits/stdc++.h>using namespace std;const int MAXN = 10005, MAXM = 100005;int n, m;struct graph {struct node {int to, next;} edge[MAXM];int head[MAXN], top, d[MAXN];graph(){top = 0; memset(head, 0, sizeof head),memset(d, 0, sizeof d); }void push(int i, int j){ d[i]++, ++top, edge[top] = (node) {j, head[i]}, head[i] = top; }} g, g1;int dfn[MAXN], low[MAXN], stk[MAXN], top = 0, vis[MAXN], dft = 0;int gp[MAXN], gtop = 0, gsiz[MAXN];set<pair<int,int> > hst;void tarjan(int nd){dfn[nd] = low[nd] = ++dft, vis[nd] = 1, stk[++top] = nd;for (int i = g.head[nd]; i; i = g.edge[i].next) {int to = g.edge[i].to;if (!dfn[to]) tarjan(to), low[nd] = min(low[nd], low[to]);else if (vis[to]) low[nd] = min(low[nd], dfn[to]);}if (low[nd] == dfn[nd]) {gtop++;int now;do {now = stk[top--];// printf("%d ", now);gp[now] = gtop, gsiz[gtop]++;vis[now] = 0;} while(now != nd); //puts("");}}void work(){int cnt = 0, ans = 0;for (int i = 1; i <= gtop; i++) if (g1.d[i] == 0) cnt++, ans += gsiz[i];if (cnt != 1) puts("0");else printf("%d\n", ans);}int main(){scanf("%d%d", &n, &m);for (int i = 1; i <= m; i++) {int u, v; scanf("%d%d", &u, &v);g.push(u, v);}for (int i = 1; i <= n; i++)if (!dfn[i])tarjan(i);for (int i = 1; i <= n; i++) {for (int j = g.head[i]; j; j = g.edge[j].next) {int to = g.edge[j].to;if (gp[to] != gp[i] && !hst.count(make_pair(gp[i],gp[to]))) {hst.insert(make_pair(gp[i], gp[to]));g1.push(gp[i], gp[to]);}}}work();return 0;}
缩点建图,取等号的边长为0,不取的边长为1。不能满足当且仅当存在长度大于1的环。
然后缩完点就变成dag上最长链了。dp做就可以。
#include <bits/stdc++.h>using namespace std;const int MAXN = 100005, MAXM = 200005;struct graph {struct node {int to, next, dis;} edge[MAXM];int head[MAXN], top;graph(){top = 0; memset(head, 0, sizeof head);}void push(int i, int j, int d){ //printf("%d--%d-->%d\n", i, d, j);++top, edge[top] = (node) {j, head[i], d}, head[i] = top; }}g, g1;int n, m;int dfn[MAXN], low[MAXN], prelen[MAXN], stk[MAXN], vis[MAXN], top = 0, dft = 0;int gp[MAXN], gsiz[MAXN], gtop = 0, len_end[MAXN];int fail = 0;void tarjan(int nd){dfn[nd] = low[nd] = ++dft, stk[++top] = nd, vis[nd] = 1;for (int i = g.head[nd]; i; i = g.edge[i].next) {int to = g.edge[i].to, d = g.edge[i].dis;if (!dfn[to]) prelen[to] = d, tarjan(to), low[nd] = min(low[nd], low[to]);else if (vis[to]) low[nd] = min(low[nd], dfn[to]), len_end[MAXN] += d;}if (dfn[nd] == low[nd]) {++gtop; int now, len = len_end[MAXN];// printf("--- Group %d ---\n", gtop);do {now = stk[top--], vis[now] = 0;// printf("%d ", now);gp[now] = gtop, gsiz[gtop]++, len += prelen[now]*(now!=nd);} while (now != nd); //puts("");if (len != 0) {fail = 1; return;}}}int dp[MAXN];int dfs(int nd){if (dp[nd] != -1) return dp[nd];dp[nd] = 0;for (int i = g1.head[nd]; i; i = g1.edge[i].next) {int to = g1.edge[i].to, d = g1.edge[i].dis;dp[nd] = max(dp[nd], dfs(to)+d);}return dp[nd] == 0 ? dp[nd] = 1 : dp[nd];}void work(){for (int i = 1; i <= n; i++)for (int j = g.head[i]; j; j = g.edge[j].next) {int to = g.edge[j].to, d = g.edge[j].dis;if (gp[i] != gp[to])g1.push(gp[to], gp[i], d); // 反图}memset(dp, -1, sizeof dp);long long ans = 0;for (int i = 1; i <= gtop; i++)ans += dfs(i)*(long long)gsiz[i];printf("%lld\n", ans);}int main(){scanf("%d%d", &n, &m);for (int i = 1; i <= m; i++) {int x, u, v; scanf("%d%d%d", &x, &u, &v);switch (x) {case 1: g.push(u, v, 0), g.push(v, u, 0); break;case 2: g.push(u, v, 1); break;case 3: g.push(v, u, 0); break;case 4: g.push(v, u, 1); break;case 5: g.push(u, v, 0); break;}}for (int i = 1; i <= n; i++)if (!dfn[i])tarjan(i);if (fail == 1) puts("-1");else work();return 0;}