Bestcoder "BestCoder"杯总结
2015-05-07 11:32:38
总结:hdu 主办的 “bc” 杯的网络赛~
12.30开始,历时5h...
比赛中搞掉了5题... room rank3 抢到一件 t 恤 0.0...
赛后补了下题... 最终8题... 本来想补一下 07,但进度不允许啦QAQ。
1001题 hdu 5214:贪心算法
题意就是根据一定规律生成 N 个区间,问能否找出三个两两相离的区间。 Li = (Li−1 ∗ a + b) mod 4294967296, Ri = (Ri−1 ∗ c + d) mod 4294967296.
观察生成规律发现,模数就是unsigned int的最大值,所以就直接用unsigned int来保存各个变量使之自然溢出即可(取模会 TLE)
接着考虑贪心法,只要找到一个最左区间和一个最右区间,再检查是否有一个区间在其中即可。而最左区间我们只考虑右端点最小值,最右区间只考虑左端点最大值。
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const ll mod = 4294967296LL; int T; unsigned int N,L,R,a,b,c,d; unsigned int Max(unsigned int a,unsigned int b){ return a > b ? a : b; } unsigned int Min(unsigned int a, unsigned int b){ return a < b ? a : b; } int main(){ scanf("%d",&T); while(T--){ int tn,tl,tr,ta,tb,tc,td; scanf("%d%d%d%d%d%d%d",&tn,&tl,&tr,&ta,&tb,&tc,&td); N = tn; L = tl; R = tr; a = ta; b = tb; c = tc; d = td; unsigned int tL = L,tR = R; unsigned int tmin = Max(L,R); unsigned int tmax = Min(L,R); for(int i = 2; i <= N; ++i){ tL = tL * a + b; tR = tR * c + d; tmin = Min(tmin,Max(tL,tR)); tmax = Max(tmax,Min(tL,tR)); } bool flag = false; tL = L; tR = R; if(Min(tL,tR) > tmin && Max(tL,tR) < tmax){ printf("YES\n"); continue; } for(int i = 2; i <= N; ++i){ tL = tL * a + b; tR = tR * c + d; if(Min(tL,tR) > tmin && Max(tL,tR) < tmax){ flag = true; break; } } if(flag) printf("YES\n"); else printf("NO\n"); } return 0; }
1002题 hdu 5215:边双连通分量,tarjan,桥,二分图染色
题意很简单,给出一个无向图,问是否存在奇环和偶环。
比赛的时候打了一发 dfs树+lca 竟然莫名其妙地过了,后来发现这是不对的,这样会导致多算(因为我把子树中的奇环都看做相交的了),比如两个相离的奇环就能 hack 掉。
正解比较多样,这里写的是题解的做法。
1. 首先通过二分图染色来找奇环。根据定理:二分图必定不存在奇环,非二分图必定存在奇环。
2. 其次用 tarjan 找出所有桥,再进行 dfs(不经过桥) 来分离出边双连通分量。
3. 对于每个边双连通分量,如果其点数 = 边数,那么必定是个简单环,直接判断环长度的奇偶性即可;
如果边数 > 点数,那么是复杂环,可以证明复杂环内必定有偶环。
(复杂环至少由两个环组成,若有小偶环则存在偶环;若只有小奇环,根据小奇环数>=2,必有两个小奇环相交,两奇环的长度 - 公共边长度×2 = 偶数,则存在大偶环。)
后记:这题用带权并查集来做会比较简便。
#pragma comment(linker, "/STACK:102400000,102400000") #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 100010; int T,N,M; int first[MAXN],ecnt; //edge int dfn[MAXN],bg[MAXN],low[MAXN],bcc[MAXN],bcnt,tot; //tarjan , bcc int ver[MAXN],edge[MAXN],co[MAXN]; //bcc , bipartite graph bool odd,even; struct Edge{ int u,v,next; }e[MAXN * 6]; void Init(){ memset(first,-1,sizeof(first)); ecnt = bcnt = tot = 0; } void add_edge(int u,int v){ e[ecnt].next = first[u]; e[ecnt].u = u; e[ecnt].v = v; first[u] = ecnt++; } void Dfs(int p,int pre){ dfn[p] = low[p] = ++tot; for(int i = first[p]; ~i; i = e[i].next){ int v = e[i].v; if(v == pre) continue; if(!dfn[v]){ Dfs(v,p); low[p] = min(low[p],low[v]); if(low[v] > dfn[p]) bg[i] = bg[i ^ 1] = 1; } else low[p] = min(low[p],dfn[v]); } } void Tarjan(){ memset(bg,0,sizeof(bg)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); for(int i = 1; i <= N; ++i) if(!dfn[i]) Dfs(i,0); } void Dfs2(int p,int pre){ ver[bcnt]++; bcc[p] = bcnt; for(int i = first[p]; ~i; i = e[i].next){ int v = e[i].v; if(bcc[v] || bg[i]) continue; Dfs2(v,p); } } void BCC(){ memset(edge,0,sizeof(edge)); memset(ver,0,sizeof(ver)); memset(bcc,0,sizeof(bcc)); for(int i = 1; i <= N; ++i) if(!bcc[i]){ ++bcnt; Dfs2(i,0); } for(int i = 0; i < ecnt; i += 2) if(bcc[e[i].u] == bcc[e[i].v]) edge[bcc[e[i].u]]++; } int Dfs_co(int p,int col){ if(co[p] == -1) co[p] = col; else return co[p]; for(int i = first[p]; ~i; i = e[i].next){ int v = e[i].v; if(co[p] == Dfs_co(v,col ^ 1)){ odd = true; break; } } return co[p]; } void Draw(){ memset(co,-1,sizeof(co)); for(int i = 1; i <= N; ++i) if(co[i] == -1){ Dfs_co(i,0); if(odd) break; } } int main(){ int a,b; scanf("%d",&T); while(T--){ Init(); scanf("%d%d",&N,&M); for(int i = 1; i <= M; ++i){ scanf("%d%d",&a,&b); add_edge(a,b); add_edge(b,a); } Tarjan(); BCC(); odd = even = false; Draw(); for(int i = 1; i <= bcnt; ++i){ if(ver[i] == edge[i] && edge[i] % 2 == 0) even = true; if(ver[i] < edge[i]) even = true; } if(odd) printf("YES\n"); else printf("NO\n"); if(even) printf("YES\n"); else printf("NO\n"); } return 0; }
1003题 hdu 5216:技巧题
比较简单的一题,找到规律就行,题意不赘述了。
仔细思考就会发现,将两个数列排序,然后对应位置形成一个个区间就行。因为交换两个数后被染黑的区间仍然一样,所以交换并没有意义。
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 1000010; int N,M,T; int A[MAXN],B[MAXN]; int vis[MAXN]; int main(){ scanf("%d",&T); while(T--){ memset(vis,0,sizeof(vis)); scanf("%d%d",&N,&M); for(int i = 1; i <= M; ++i) scanf("%d",&A[i]); for(int i = 1; i <= M; ++i) scanf("%d",&B[i]); sort(A + 1,A + M + 1); sort(B + 1,B + M + 1); bool flag = true; for(int i = 1; i <= M; ++i){ if(A[i] > B[i]){ flag = false; break; } for(int j = A[i]; j <= B[i]; ++j) vis[j] = 1; } if(flag == false) printf("Stupid BrotherK!\n"); else{ int cur = 0,tmax = 0; for(int i = 1; i <= N; ++i){ if(!vis[i]) cur++; else{ tmax = max(tmax,cur); cur = 0; } } tmax = max(tmax,cur); printf("%.6f\n",(double)tmax); } } return 0; }
1004题 hdu 5217:线段树*,技巧,分段处理
一道线段树中难题,比赛中过的人不多...
题意:给出N个括号,每次询问给出一个区间[l,r]以及k,问将区间内左右匹配的括号约掉之后,第k个括号在什么位置。
思路:用一颗线段树维护每个区间约掉匹配的括号后还剩几个 '(' 和几个 ')',我们可以发现约掉之后必然是:" )))..(((" 这种形式,那么
区间就可以合并了(通过简单计算可以知道两个区间合并后的 '(' 和 ')' 个数)。
※对于修改,很容易进行。
※对于每个询问:我们可以按照线段树将这个区间分成若干块(logn级别... 这个过程有点像分块,不过分块是sqrtn),然后我们需要
发现两个单调性:从左到右合并区间 ')' 的个数不会减少,从右到左合并区间 '(' 的个数不会减少。那么我们只要先预处理一下区间里面
的所有块,统计出 '(' 和 ')' 的个数,如果左右括号的总数 < k,那么答案为-1,不然看 ')' 的个数是否大于 k,如果是那么第k个为 ')',否则第k个为 '('。
(1)如果第k个为 ')' 右括号,那么从左到右扫一遍所有块,确定第k个右括号在哪一块。
然后开始 dfs ,利用线段树计算一下答案位于左半部分还是右半部分,深入下去,最终找到答案,logn的深度。
(2)如果第k个为 '(' 左括号,类似。
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 200010; const int MAX_LOG = 100; int T,N,Q; char s[MAXN]; int tL[MAXN << 2],tR[MAXN << 2]; int block[MAX_LOG][3],bcnt; inline void Push_up(int p){ tR[p] = tR[p << 1] + max(0,tR[p << 1|1] - tL[p << 1]); tL[p] = tL[p << 1|1] + max(0,tL[p << 1] - tR[p << 1|1]); } inline void Build_tree(int p,int l,int r){ if(l == r){ tL[p] = s[l] == '(' ? 1 : 0; tR[p] = s[l] == ')' ? 1 : 0; return; } int mid = getmid(l,r); Build_tree(p << 1,l,mid); Build_tree(p << 1|1,mid + 1,r); Push_up(p); } inline void Update_tree(int a,int p,int l,int r){ if(l == r){ tL[p] ^= 1; tR[p] ^= 1; return; } int mid = getmid(l,r); if(a <= mid) Update_tree(a,p << 1,l,mid); else Update_tree(a,p << 1|1,mid + 1,r); Push_up(p); } void Block(int a,int b,int p,int l,int r){ if(a <= l && r <= b){ block[++bcnt][0] = p; block[bcnt][1] = l; block[bcnt][2] = r; return; } int mid = getmid(l,r); if(a <= mid) Block(a,b,p << 1,l,mid); if(b > mid) Block(a,b,p << 1|1,mid + 1,r); } inline int Query_pre_tree(int curL,int curR,int k,int p,int l,int r){ if(l == r) return l; int mid = getmid(l,r); int nxtR = curR + max(0,tR[p << 1] - curL); int nxtL = tL[p << 1] + max(0,curL - tR[p << 1]); if(nxtR >= k) return Query_pre_tree(curL,curR,k,p << 1,l,mid); else return Query_pre_tree(nxtL,nxtR,k,p << 1|1,mid + 1,r); } inline int Query_suf_tree(int curL,int curR,int k,int p,int l,int r){ if(l == r) return l; int mid = getmid(l,r); int nxtR = tR[p << 1|1] + max(0,curR - tL[p << 1|1]); int nxtL = curL + max(0,tL[p << 1|1] - curR); if(nxtL >= k) return Query_suf_tree(curL,curR,k,p << 1|1,mid + 1,r); else return Query_suf_tree(nxtL,nxtR,k,p << 1,l,mid); } int main(){ int op,l,r,k; scanf("%d",&T); while(T--){ scanf("%d%d",&N,&Q); scanf("%s",s + 1); Build_tree(1,1,N); for(int q = 1; q <= Q; ++q){ scanf("%d",&op); if(op == 1){ scanf("%d",&k); Update_tree(k,1,1,N); } else{ scanf("%d%d%d",&l,&r,&k); bcnt = 0; Block(l,r,1,1,N); //printf("res : %d %d\n",res.first,res.second); int ans,p,curL = 0,curR = 0,preL,preR; for(int i = 1; i <= bcnt; ++i){ p = block[i][0]; preR = curR,preL = curL; curR += max(0,tR[p] - preL); curL = tL[p] + max(0,preL - tR[p]); if(curR >= k){ ans = Query_pre_tree(preL,preR,k,p,block[i][1],block[i][2]); break; } } if(curR >= k){ printf("%d\n",ans); continue; } if(curL + curR < k){ printf("-1\n"); continue; } k = curL + curR + 1 - k; curL = curR = 0; for(int i = bcnt; i >= 1; --i){ p = block[i][0]; preR = curR,preL = curL; curR = tR[p] + max(0,preR - tL[p]); curL += max(0,tL[p] - preR); if(curL >= k){ ans = Query_suf_tree(preL,preR,k,p,block[i][1],block[i][2]); break; } } printf("%d\n",ans); } } } return 0; }
1005题 hdu 5218:约瑟夫环,dp
题意:N个人按编号顺时针站成一圈,给出一列M个数A[1~M],总共N-1轮游戏,初始位置为pos=1,每轮游戏从M个数中任意挑选一个A[i],并将位置挪到
pos+A[i](顺时针移动,N与1相邻),该位置的人出局,如此循环N-1轮,最后剩下的人胜利。问有多少个人可能作为最后的胜利者。
思路:经典的约瑟夫环问题,QAQ公式记错了... 比赛中又懒得推了... 最后百度了约瑟夫环的公式。
有逆推公式: f[i] = (f[i - 1] + m) % i (i > 1) , f[i] 表示 i 个人做游戏,最后留下的人的编号。
※这题有了一点变化,所以用 dp[i][j] 表示剩 i 个人编号为 j 的人胜出的可能性,可能的话为1,不可能为0。
初始化:dp[1][0] = 1(假设编号0~N-1)
dp[i][j]=1 ----转移给---> dp[i+1][(j+A[k])%(i+1)]=1,只要枚举一下k即可。
复杂度O(N^3)
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 210; int N,M,T; int A[MAXN]; int dp[MAXN][MAXN]; int main(){ scanf("%d",&T); while(T--){ scanf("%d%d",&N,&M); for(int i = 1; i <= M; ++i) scanf("%d",&A[i]); memset(dp,0,sizeof(dp)); dp[1][0] = 1; for(int i = 1; i < N; ++i){ for(int j = 0; j < N; ++j){ if(dp[i][j] == 0) continue; for(int k = 1; k <= M; ++k){ dp[i + 1][(j + A[k]) % (i + 1)] = 1; } } } int cnt = 0; for(int j = 0; j < N; ++j) cnt += dp[N][j]; printf("%d\n",cnt); int cur = 0; for(int j = 0; j < N; ++j) if(dp[N][j]){ ++cur; printf("%d",j + 1); if(cur == cnt) printf("\n"); else printf(" "); } } return 0; }
1008题 hdu 5219:树链剖分,线段树,区间更新/查询
比较明显的树链剖分题。
首先根据点权建树,保存区间点权和 tsum[],以及区间有效点权和 t[](只计算属于 Miceren 的点)。
※对于操作一,区间赋值分体,给每个节点保存一个变量 co,表示区间里的所有点是否都都属于 Miceren 。
※对于操作二,单点更新 co 值
※对于操作三,仍然是区间赋值,由于一个点的子树在线段树里的编号是连续了,所以更新 p 点的子树,其对应的区间是 [p,p + size[p] - 1],size[p] 表示以 p 为根的子树的大小。
#pragma comment(linker, "/STACK:102400000,102400000") #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 100010; int T,N,Q; int val[MAXN]; int first[MAXN],ecnt; int dep[MAXN],sz[MAXN],son[MAXN],fa[MAXN],top[MAXN],w[MAXN],aw[MAXN],tsz; int t[MAXN << 2],co[MAXN << 2],tsum[MAXN << 2]; struct edge{ int v,next; }e[MAXN << 1]; void add_edge(int u,int v){ e[ecnt].next = first[u]; e[ecnt].v = v; first[u] = ecnt++; } void Init(){ ecnt = tsz = 0; memset(first,-1,sizeof(first)); memset(co,0,sizeof(co)); memset(t,0,sizeof(t)); } void Dfs(int p,int pre,int d){ sz[p] = 1; son[p] = -1; fa[p] = pre; dep[p] = d; int v,tmp = 0; for(int i = first[p]; ~i; i = e[i].next){ int v = e[i].v; if(v == pre) continue; Dfs(v,p,d + 1); if(sz[v] > tmp){ tmp = sz[v]; son[p] = v; } sz[p] += sz[v]; } } void Dfs_pos(int p,int tp){ w[p] = ++tsz; aw[tsz] = p; top[p] = tp; if(son[p] != -1) Dfs_pos(son[p],tp); for(int i = first[p]; ~i; i = e[i].next){ int v = e[i].v; if(v != son[p] && v != fa[p]) Dfs_pos(v,v); } } void Push_down(int a){ if(co[a]){ co[a << 1] = co[a << 1|1] = 1; t[a << 1] = tsum[a << 1]; t[a << 1|1] = tsum[a << 1|1]; co[a] = 0; t[a] = 0; } } void Update_tree(int a,int b,int c,int p,int l,int r){ if(a <= l && r <= b){ co[p] = c; t[p] = co[p] == 1 ? tsum[p] : 0; return; } Push_down(p); int mid = getmid(l,r); if(a <= mid) Update_tree(a,b,c,p << 1,l,mid); if(b > mid) Update_tree(a,b,c,p << 1|1,mid + 1,r); t[p] = t[p << 1] + t[p << 1|1]; co[p] = co[p << 1] & co[p << 1|1]; } void Change_1(int a,int b){ int f1 = top[a],f2 = top[b]; while(f1 != f2){ if(dep[f1] > dep[f2]){ swap(a,b); swap(f1,f2); } Update_tree(w[f2],w[b],1,1,1,tsz); b = fa[f2]; f2 = top[b]; } if(dep[a] > dep[b]) swap(a,b); Update_tree(w[a],w[b],1,1,1,tsz); } void Change_2(int a){ Update_tree(w[a],w[a],0,1,1,tsz); } void Change_3(int a){ Update_tree(w[a],w[a] + sz[a] - 1,1,1,1,tsz); } void Build_tree(int p,int l,int r){ if(l == r){ tsum[p] = val[aw[l]]; return; } int mid = getmid(l,r); Build_tree(p << 1,l,mid); Build_tree(p << 1|1,mid + 1,r); tsum[p] = tsum[p << 1] + tsum[p << 1|1]; } int main(){ int a,b,c; scanf("%d",&T); while(T--){ Init(); scanf("%d",&N); for(int i = 1; i <= N; ++i) scanf("%d",&val[i]); for(int i = 1; i < N; ++i){ scanf("%d%d",&a,&b); add_edge(a,b); add_edge(b,a); } Dfs(1,0,0); Dfs_pos(1,1); Build_tree(1,1,tsz); scanf("%d",&Q); for(int i = 1; i <= Q; ++i){ scanf("%d%d",&a,&b); if(a == 1){ scanf("%d",&c); Change_1(b,c); } else if(a == 2) Change_2(b); else Change_3(b); printf("%d\n",t[1]); } } return 0; }
1009题 hdu 5222:混合图找环
给出混合图,判断其中是否有环,每条边只能经过一遍(包括无向边,经过某个方向后就不能从反向再走一次)
方法比较多:(1)改进版的 Dfs,每个点有三种状态,0表示没有遍历过,-1已经遍历但其后继节点未遍历完,1表示该点以及其后继节点都遍历完毕。
(2)拓扑排序
(3)tarjan
这里用 Dfs 实现。
#pragma comment(linker, "/STACK:102400000,102400000") #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 1000010; int T,N,M1,M2,ecnt; int first[MAXN]; int used[MAXN]; int ans,vis[MAXN << 2]; struct edge{ int v,next,id; }e[MAXN << 2]; void add_edge(int u,int v,int id){ e[ecnt].id = id; e[ecnt].next = first[u]; e[ecnt].v = v; first[u] = ecnt++; } void Dfs(int p){ if(ans) return; used[p] = -1; for(int i = first[p]; ~i; i = e[i].next){ int v = e[i].v; if(vis[i]) continue; if(e[i].id == 1) vis[i] = vis[i ^ 1] = 1; else vis[i] = 1; if(used[v] == -1){ ans = true; return; } Dfs(v); } used[p] = 1; } int main(){ int a,b; scanf("%d",&T); while(T--){ scanf("%d%d%d",&N,&M1,&M2); memset(first,-1,sizeof(first)); ecnt = 0; for(int i = 1; i <= M1; ++i){ scanf("%d%d",&a,&b); add_edge(a,b,1); add_edge(b,a,1); } for(int i = 1; i <= M2; ++i){ scanf("%d%d",&a,&b); add_edge(a,b,2); } memset(used,0,sizeof(used)); memset(vis,false,sizeof(vis)); ans = false; for(int i = 1; i <= N; ++i) if(!used[i]){ Dfs(i); if(ans) break; } if(ans) printf("YES\n"); else printf("NO\n"); } return 0; }
1010题 hdu 5223:数学题
题意:给出若干个区间 gcd 答案,让你恢复出这个数列。
想法比较自然,首先给数列里的每个数赋为1。
对于每个询问,把 gcd 答案和区间里每一个数做 lcm ,并把 lcm 赋值给数列里的数。(因为区间里的每个数是该 gcd 的倍数。)
最后检查一下该数列是否符合每个询问的答案即可。
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; int T; int N,Q; int l[1010],r[1010],ans[1010]; ll v[1010]; ll Gcd(ll a,ll b){ while(a > 0 && b > 0){ if(a > b) a %= b; else b %= a; } return a + b; } ll Lcm(ll a,ll b){ return a / Gcd(a,b) * b; } int main(){ scanf("%d",&T); while(T--){ scanf("%d%d",&N,&Q); for(int i = 1; i <= N; ++i) v[i] = 1; for(int i = 1; i <= Q; ++i){ scanf("%d%d%d",&l[i],&r[i],&ans[i]); for(int j = l[i]; j <= r[i]; ++j){ v[j] = Lcm(v[j],ans[i]); } } //for(int i = 1; i <= N; ++i) // printf("%lld ",v[i]); //puts(""); bool flag = true; for(int i = 1; i <= Q; ++i){ ll cur = v[l[i]]; for(int j = l[i] + 1; j <= r[i]; ++j) cur = Gcd(cur,v[j]); if(cur != ans[i]){ flag = false; break; } } if(flag == false) printf("Stupid BrotherK!\n"); else{ printf("%I64d",v[1]); for(int i = 2; i <= N; ++i) printf(" %I64d",v[i]); puts(""); } } return 0; }