卡常火车头
学长(wljss)那搬来的hhh
#pragma GCC diagnostic error "-std=c++11"
#pragma GCC target("avx")
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
线段树2
#include<bits/stdc++.h>
#define _ 0
#define N 100001
#define mid ((l + r) >> 1)
#define pl (p << 1)
#define pr (p << 1) | 1
using namespace std;
typedef long long ll;
ll tr[N * 5], ta[N * 5], la[N * 5], o[N], mod;
void up(int p){ tr[p] = tr[pl] + tr[pr]; }
void down(int l, int r, int p){
if(la[p] != 1){
la[pl] = la[pl] * la[p] % mod; la[pr] = la[pr] * la[p] % mod;
ta[pl] = ta[pl] * la[p] % mod; ta[pr] = ta[pr] * la[p] % mod;
tr[pl] = tr[pl] * la[p] % mod; tr[pr] = tr[pr] * la[p] % mod;
la[p] = 1;
}
if(ta[p]){
ta[pl] = (ta[pl] + ta[p]) % mod; ta[pr] = (ta[pr] + ta[p]) % mod;
tr[pl] = (tr[pl] + (mid - l + 1) * ta[p]) % mod;
tr[pr] = (tr[pr] + (r - mid) * ta[p]) % mod;
ta[p] = 0;
}
}
void mul(int s, int t, int l, int r, ll c, int p){
if(s <= l and t >= r) {
la[p] = la[p] * c % mod; ta[p] = ta[p] * c % mod;
tr[p] = tr[p] * c % mod; return;
}down(l, r, p);
if(mid >= s) mul(s, t, l, mid, c, pl);
if(mid < t) mul(s, t, mid + 1, r, c, pr);
up(p);
}
void add(int s, int t, int l, int r, ll c, int p){
if(s <= l and t >= r) {
ta[p] = (ta[p] + c) % mod;
tr[p] = (tr[p] + c * (r - l + 1)) % mod;
return;
}down(l, r, p);
if(mid >= s) add(s, t, l, mid, c, pl);
if(mid < t) add(s, t, mid + 1, r, c, pr);
up(p);
}
ll query(int s, int t, int l, int r, int p){
ll sum = 0;
if(s <= l and t >= r) return tr[p];
down(l, r, p);
if(mid >= s) sum = (sum + query(s, t, l, mid, pl)) % mod;
if(mid < t) sum = (sum + query(s, t, mid + 1, r, pr)) % mod;
return sum;
}
void construct(int l, int r, int p){
la[p] = 1;
if(l == r) { tr[p] = o[l]; return; }
construct(l, mid, pl); construct(mid + 1, r, pr); up(p);
}
int main(){
int n, m, op, x, y; ll k;
cin >> n >> m >> mod;
for(int i = 1; i <= n; i ++) cin >> o[i];
construct(1, n, 1);
for(int i = 1; i <= m; i ++){
cin >> op >> x >> y;
if(op == 1){ cin >> k; mul(x, y, 1, n, k, 1); }
if(op == 2){ cin >> k; add(x, y, 1, n, k, 1); }
if(op == 3) cout << query(x, y, 1, n, 1) << "
";
}
return ~~(0^_^0);
}
枚举子集(二进制
int S = 2333;
for(int i = S; i; i = (i - 1) & S)
cout << bitset<10>(i) << endl;
快读快写模板
inline void read(int &x){
x=0;register char c=getchar();
while(c<'0'||c>'9'){c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
}
inline void write(int x){
if(x < 0) putchar('-'),x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
快速幂&慢速乘
ll msc(ll a, ll b, ll p){
ll res = 0;
while(b){
if(b & 1) res = (res + a) % p;
a = (a + a) % p;
b = b >> 1;
}
return res;
}
ll ksm(ll a, ll b, ll p){
a %= p;
ll res = 1;
while(b > 0){
if(b & 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
树剖
opt 1:x y z 表示 x 到 y 所有节点权值加 z
opt 2:x y 表示求 x 到 y 所有节点的权值之和
opt 3:x z 表示以 x 为根节点的子树内所有节点权值加 z
opt 4:x 表示求以 x 为根节点的子树内所有节点权值之和
#include <bits/stdc++.h>
#define N 500010
#define il inline
using namespace std;
typedef long long ll;
il int in() {
int x = 0, f = 1; char C = getchar();
while(C < '0' or C > '9') { if(C == '-') f = -1; C = getchar(); }
while(C >= '0' and C <= '9') { x = (x << 3) + (x << 1) + (C ^ 48); C = getchar();}
return x * f;
}
int f[N], dep[N], sz[N], son[N], dfn[N], top[N], cnt[N], id[N], tot;
//cnt:该子树最大节点编号(线段树上)dfn映射原标号与新标号
int n, m, root, val[N], mod; //val:节点权值
int e_cnt, to[N << 1], mrk[N << 1], head[N];
struct hh { ll c, f; } t[N << 2]; //c:区间和 f:懒标记
il void add(int x, int y) {
to[++ e_cnt] = y, mrk[e_cnt] = head[x], head[x] = e_cnt;
}
//---------------------------------
//找重儿子
void dfs1(int u) {
sz[u] = 1;
for(int i = head[u]; i; i = mrk[i]) {
int v = to[i];
if(v == f[u]) continue;
f[v] = u;
dep[v] = dep[u] + 1;
dfs1(v);
sz[u] += sz[v];
if(sz[v] > sz[son[u]]) son[u] = v;
}
}
//找链头
void dfs2(int u, int tp) {
top[u] = tp;
dfn[u] = ++ tot;
id[tot] = u;
if(son[u]) dfs2(son[u], tp);
for(int i = head[u]; i; i = mrk[i]) {
int v = to[i];
if(v != son[u] and v != f[u]) dfs2(v, v);
}
cnt[u] = tot;
}
//----------------------------------
//线段树
#define mid ((l + r) >> 1)
#define pl p << 1
#define pr p << 1 | 1
il void up(int p) { t[p].c = t[pl].c + t[pr].c; }
void build(int l, int r, int p) {
if(l == r) { t[p].c = val[id[l]]; return; } //线段树上用的是新编号
build(l, mid, pl), build(mid + 1, r, pr);
up(p);
}
il void down(int l, int r, int p) {
t[pl].f += t[p].f, t[pr].f += t[p].f;
t[pl].c += (mid - l + 1) * t[p].f;
t[pr].c += (r - mid) * t[p].f;
t[p].f = 0;
}
void change(int x, int y, int l, int r, int p, ll c) {
if(x <= l and r <= y) {
t[p].c += (r - l + 1) * c;
t[p].f += c;
return;
}
if(t[p].f) down(l, r, p);
if(mid >= x) change(x, y, l, mid, pl, c);
if(mid < y) change(x, y, mid + 1, r, pr, c);
up(p);
}
ll query(int x, int y, int l, int r, int p) {
if(x <= l and r <= y) return t[p].c;
if(t[p].f) down(l, r, p);
ll ans = 0;
if(mid >= x) ans += query(x, y, l, mid, pl), ans %= mod;
if(mid < y) ans += query(x, y, mid + 1, r, pr), ans %= mod;
return ans;
}
//-----------------------------------
//opt1
il void xy() {
int x = in(), y = in(), z = in();
while(top[x] != top[y]) {
if(dep[top[x]] > dep[top[y]]) swap(x, y);
change(dfn[top[y]], dfn[y], 1, tot, 1, (ll)z);
y = f[top[y]];
}
if(dep[x] > dep[y]) swap(x, y);
change(dfn[x], dfn[y], 1, tot, 1, (ll)z);
}
//opt2
il void xxyy() {
int x = in(), y = in();
ll ans = 0;
while(top[x] != top[y]) {
if(dep[top[x]] > dep[top[y]]) swap(x, y);
ans = (ans + query(dfn[top[y]], dfn[y], 1, tot, 1)) % mod;
y = f[top[y]];
}
if(dep[x] > dep[y]) swap(x, y);
ans = (ans + query(dfn[x], dfn[y], 1, tot, 1)) % mod;
cout << ans << "
";
}
//opt3
il void cx() {
int x = in(), y = in();
change(dfn[x], cnt[x], 1, tot, 1, (ll)y);
}
//opt4
il void cxx() {
int x = in();
cout << query(dfn[x], cnt[x], 1, tot, 1) << "
";
}
//---------------------------------
int main() {
n = in(), m = in(), root = in(), mod = in();
for(int i = 1; i <= n; i ++) val[i] = in();
for(int i = 1, x, y; i < n; i ++) {
x = in(), y = in();
add(x, y), add(y, x);
}
dfs1(root);
dfs2(root, root);
build(1, tot, 1);
for(int i = 1, op; i <= m; i ++) {
op = in();
if(op == 1) xy();
else if(op == 2) xxyy();
else if(op == 3) cx();
else cxx();
}
return 0;
}
Manacher
精简版
const int maxn=1000010;
char str[maxn];//原字符串
char tmp[maxn<<1];//转换后的字符串
int Len[maxn<<1];
//转换原始串
int INIT(char *st)
{
int i,len=strlen(st);
tmp[0]='@';///开头加一个不等于#也不等于字符串的字符,这样就不用判断左边越界了,那么右边万一比到n+1怎么办呢?有 吗?不, 在n处,解决办法看16行
for(i=1;i<=2*len;i+=2)
{
tmp[i]='#';
tmp[i+1]=st[i/2];
}
tmp[2*len+1]='#';
tmp[2*len+2]='$';///结尾搞一个不是@也不是#的字符
tmp[2*len+3]=0;
return 2*len+1;//返回转换字符串的长度
}
//Manacher算法计算过程
int MANACHER(char *st,int len)
{
int mx=0,ans=0,po=0;//mx即为当前计算回文串最右边字符的最大值
for(int i=1;i<=len;i++)
{
if(mx>i)
Len[i]=min(mx-i,Len[2*po-i]);//在Len[j]和mx-i中取个小
else
Len[i]=1;//如果i>=mx,要从头开始匹配
while(st[i-Len[i]]==st[i+Len[i]])
Len[i]++;
if(Len[i]+i>mx)//若新计算的回文串右端点位置大于mx,要更新po和mx的值
{
mx=Len[i]+i;
po=i;
}
ans=max(ans,Len[i]);
}
return ans-1;//返回Len[i]中的最大值-1即为原串的最长回文子串额长度
}
KMP
#include<bits/stdc++.h>
const int N = 1e6+5;
using namespace std;
int nt[N];
char s[N];
char ss[N];
int KMP() { ///求大串中有几个子串
int len=strlen(s);
int t=strlen(ss);
nt[0]=-1;
int cnt=0;
///构造next数组
for (int i=0,j=-1; i<t; ){
if (j==-1 || ss[i]==ss[j]) i++, j++, nt[i]=j;
else j=nt[j];
}
for (int i=0;i<t;i++) printf("nt[%d]=%d ",i,nt[i]); printf("
");
///开始遍历大串找其中的小串
for (int i=0,j=0; i<len; ){
if (j==-1 || ss[j]==s[i]) i ++, j ++;
else j=nt[j];
if (j==t){
cnt++;
j=0;
}
}
return cnt;
}
int main() {
while (cin>>s) {
cin>>ss;
cout<<KMP()<<endl;
}
}
邻接表存字典树
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int N = 4000*1001 + 5;
struct node {
int son, right, sum;
char ch;
}trie[N];
int id; ll ans;
void init() {
ans = 0; id = 1;
trie[0].right = trie[0].son = trie[0].sum = 0;
}
void insert(char *s) {
int u = 0,j;
int len = strlen(s);
for (int i=0;i<=len;i++){
bool flag = false;
for (j=trie[u].son;j!=0;j=trie[j].right)
if (s[i]==trie[j].ch) { flag = true; break; }
if (!flag){
j = id++;
trie[j].right = trie[u].son;
trie[u].son = j;
trie[j].ch = s[i];
trie[j].son = trie[j].sum = 0;
}
ans += (trie[u].sum+trie[j].sum);
if (i==len) trie[j].sum++;
trie[u].sum++;
u = j;
}
}
int main() {
int n; char in[1010];
for(int kca = 1; scanf("%d", &n), n; kca ++) {
init();
while (n--) scanf("%s",in),insert(in);
printf("Case %d: %lld
",kca,ans);
}
}
矩阵快速幂
#include<bits/stdc++.h>
#define mod 1000000007
#define N 102
#define ll long long
using namespace std;
int n;
struct hh{
ll a[N][N];
hh(){ memset(a, 0, sizeof(a)); }
inline void build(){
for(int i = 1; i <= n; i ++)
a[i][i] = 1;
}
} a;
hh operator *(const hh &x, const hh & y){
hh z;
for(int s = 1; s <= n; s ++)
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
z.a[i][j] = (z.a[i][j] + x.a[i][s] * y.a[s][j] % mod) % mod;
return z;
}
ll k;
int main(){
cin >> n >> k;
hh ans; ans.build();
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
cin >> a.a[i][j];
while(k){
if(k & 1) ans = ans * a;
a = a * a;
k >>= 1;
}
for(int i = 1; i <= n; putchar('
'), i ++)
for(int j = 1; j <= n; j ++)
cout << ans.a[i][j] << " ";
return 0;
}
堆优化dijkstra 算法
const int N = 1e5+5;///点的数量
const int M = 5e5+5;///边的数量
struct Edge { int to, last, w; }edge[M];
int head[N],id;
void add(int u,int v,int w) {//建从u->v,权值为w的边
edge[id].to = v;
edge[id].w = w;
edge[id].last = head[u];
head[u] = id++;
}
void init() {//建边前的初始化
memset(head,0,sizeof head);
id = 1;
}
struct node {//储存点
int now;
int w;//到达now节点的这条边的权值
bool operator < (const node& a)const{//***比较方式要和自己想的反过来***
return w>a.w;
}
};
bool vis[N];//是否求出最短路径
void dijkstra() {
memset(vis,0,sizeof vis);
priority_queue<node>que;
int root = 1;//单元最短路径的源点
que.push({root,0});
while (!que.empty()){
node now = que.top();que.pop();
if (vis[now.now]) continue;
vis[now.now] = true;
/*
当前点now记录了这个点到源点的最短距离,问题可以在这儿处理
*/
for (int i=head[now.now];i!=0;i=edge[i].last){
que.push({edge[i].to,edge[i].w+now.w});//***权值记得要加now.w***
}
}
}
SPFA
const int N = 1e4+5;
const int M = 5e4+5;
struct Edge { int to,last,w; }edge[M];
int id,head[N];
void add(int u,int v,int w) {
edge[id].to = v; edge[id].w = w; edge[id].last = head[u]; head[u] = id++;
}
void init() { id = 1; memset(head,0,sizeof head); }
int val[N];//记录该节点被更新多少次
int dis[N];//记录单源最短路
void SPFA(int V) { //V表示节点数
memset(dis,INF,sizeof dis);
memset(val,0,sizeof val);
int root = 1;//若只用于判负环,源点可以随便取
bool flag = false;//是否有负环,初始化为无
dis[root] = 0;
/* 下面注释掉的四行是SPFA的优化,节点数多的时候一般来说是会优化...
就是先让dis[]小的去松弛,这样可以让进队列的数更少 */
//deque<int>que;
//que.pb(root);
queue<int>que;
que.push(root);
while (!que.empty()){
int now = que.front();que.pop_front();
int v;
for (int i=head[now];i!=0;i=edge[i].last){
v = edge[i].to;
if (dis[v]>dis[now]+edge[i].w){
dis[v] = dis[now] + edge[i].w;
//que.pb(v);
//if (dis[que.front()]<dis[que.back()]){que.pf(v);que.pop_back();}
que.push(v);
val[v]++;
if (val[v]==V) {flag = true;break;}
}
}
if (flag) break;
}
}
动态开点线段树
求逆序
#include<bits/stdc++.h>
using namespace std;
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define ll long long
#define mid int m = l+r>>1
#define tl tree[rt].l
#define tr tree[rt].r
const int N = 5e5+5, maxn = 500000*32 + 5, MAXR = 1e9;
struct node { int l,r; int sum; }tree[maxn]; int sz;
void init(){sz = 2;}
void push_up(int rt) { tree[rt].sum = tree[tl].sum + tree[tr].sum; }
void update(int p,int& rt,int l,int r) {
//printf("update[%d,%d]
",l,r);
if (!rt){ rt = sz++; tl = tr = tree[rt].sum = 0; }
if (l==r){ tree[rt].sum++; return; }
mid; if (p<=m) update(p,tl,l,m);
else update(p,tr,m+1,r);
push_up(rt);
}
ll query(int L,int R,int rt,int l,int r) {
//printf("query[%d,%d]
",l,r);
if (!rt) return 0;
if (L<=l && r<=R) return tree[rt].sum;
ll ans = 0; mid;
if (L<=m) ans += query(L,R,tl,l,m);
if (R>m) ans += query(L,R,tr,m+1,r);
return ans;
}
int main() {
init(); int n; scanf("%d",&n);
ll ans = 0; int root = 1;
for1(i,1,n){
int x; scanf("%d",&x);
if (x+1<=MAXR) ans += query(x+1,MAXR,1,1,MAXR);//防止区间无效
update(x,root,1,MAXR);
} printf("%lld
",ans);
return 0;
}
可持久化线段树(主席树)
静态区间第k小
int a[N], data[N];
void discrete(int n) {//使用该函数把a[i]变成原本a[i]离散化后对应的数,data可以根据离散化后的值推实际的大小
for1(i,1,n) data[i] = a[i];
sort(data+1,data+1+n);
int cnt = unique(data+1,data+1+n) - data;
for1(i,1,n) a[i] = lower_bound(data+1,data+cnt,a[i]) - data;
}
struct node { int l, r, sum; }tree[M];
int sz,root[N];
void push_up(int rt){ tree[rt].sum = tree[tl].sum + tree[tr].sum; }
void update(int old,int p,int& rt,int l,int r) {
rt = sz++;//这里容易脑抽写成if(!rt) rt = sz++
if (l==r){ tree[rt].sum = tree[old].sum + 1; return ; }
tree[rt] = tree[old]; mid;
if (p<=m) update(tl,p,lson);
else update(tr,p,rson);
push_up(rt);
} int ccnt;
int query(int old,int k,int rt,int l,int r) {
if (l==r) return data[l]; mid;
ccnt = tree[tl].sum - tree[tree[old].l].sum;
if (ccnt >= k) return query(tree[old].l,k,lson);
else return query(tree[old].r,k-ccnt,rson);
}
int main() {
int n,m; scanf("%d %d",&n,&m);
sz = 1;
for1(i,1,n) scanf("%d",a+i);
discrete(n);
for1(i,1,n) update(root[i-1],a[i],root[i],1,n);
int l,r,k;
for1(i,1,m){
scanf("%d %d %d",&l,&r,&k);
printf("%d
",query(root[l-1],k,root[r],1,n));
}
return 0;
}
随机生成一棵树 + 随机生成两个节点询问路径第k小(对拍用)
//不保证一定没有问题
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
const int maxINT = ((1LL<<31)-1);
const int N = 1e5+5;
int Rand(bool tag=false){//生成int范围随机数,tag=true表示允许负数
int s1 = rand();
int s2 = rand();
int flag = 1;
if (tag)flag = rand()%2==0? 1:-1;
return 1LL*flag*s1*s2%maxINT;
}
//**************************************************************************
//建树
struct E{
int to,last;
}edge[N<<1];
int head[N],id;
void add(int u,int v){edge[id].to = v;edge[id].last = head[u];head[u] = id++;}
//***********************************************************************************
//并查集判断两个点是否在同一个集合中
int uf[N];
int find1(int x){
int r = x;
while (uf[r]!=r) r = uf[r];
for (int i = x,j;i!=r;i=j){
j = uf[i];
uf[i] = r;
}
return r;
}
void join(int a,int b){
a = find1(a),b = find1(b);
if (a!=b) uf[a] = b;
}
//************************************************************************************
int dep[N],lca[N][20];
void dfs(int now,int f,int depth){
lca[now][0] = f;
dep[now] = depth;
for (int i=head[now];i!=0;i=edge[i].last){
int v = edge[i].to;
if (v==f) continue;
dfs(v,now,depth+1);
}
}
void getst(int n){
for (int j=1;(1<<j)<=n;j++)
for (int i=1;i<=n;i++)
lca[i][j] = lca[lca[i][j-1]][j-1];
}
int getlca(int x,int y){
if (dep[x]<dep[y]) swap(x,y);
int differ = dep[x]-dep[y];
for (int i=0;(1<<i)<=differ;i++){
if ((1<<i)&differ){
x = lca[x][i];
}
}
if (x==y) return x;
for (int i=18;i>=0;i--){
if (lca[x][i]!=lca[y][i]) x = lca[x][i],y = lca[y][i];
}
return lca[x][0];
}
//**************************************************************************************
int main()
{
srand(time(0));
freopen("C:/Users/DELL/Desktop/input.txt", "w", stdout);//输入想要保存文件的路径
/*u*/ //修改数据组数
int T = 100;//数据组数,可修改
printf("%d
",T);
/*d*/
/*u*/ //修改生成的树最多节点个数,必须<=1e5
int maxsize = 20;
/*d*/
while(T--){
/*u*/ //修改当前这组数据的节点数以及询问次数,q默认10次
int n = Rand()%(maxsize+1),q=10;
if (!n) n++;
/*d*/
printf("%d %d
",n,q);
for1(i,1,n) uf[i] = i,head[i] = 0;
memset(lca,0,sizeof lca);
id = 1;
for1(i,1,n){
if (i!=1) printf(" ");
printf("%d",Rand(true));
}puts("");//生成每个节点的值
int hulue = Rand()%(n+1);
if (!hulue) hulue++;
for1(i,1,n){
if (i==hulue) continue;
int v;
do {
v = Rand()%(n+1);
if (!v) v++;
}while (find1(i)==find1(v));
join(i,v);//并查集
printf("%d %d
",i,v);//生成数据
add(i,v);add(v,i);//生成树,用于check合法的k
}//随机生成一棵树结束
dfs(1,0,1);
getst(n);
while(q--){
int u = Rand()%(n+1),v = Rand()%(n+1);
if (!u) u++;if (!v) v++;
int LCA = getlca(u,v);
int maxk = dep[u]-dep[LCA]+dep[v]-dep[LCA]+1;
int k = Rand()%(maxk+1);
if (!k) k++;
printf("%d %d %d
",u,v,k);
}
}
return 0;
}