AtCoder Beginner Contest 355 赛后总结

比赛链接

非常美丽地在1h内做完了前4题,结果第五题交互题,死活不对。。。

题解

A. Who Ate the Cake?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include<bits/stdc++.h>

using namespace std;

#define ll long long

int a,b;

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>a>>b;
if(a!=b)
{
cout<<6-a-b<<"\n";
}
else
{
cout<<-1<<"\n";
}

return 0;
}

B. Piano 2

直接排序然后对比即可,就是要注意C数组的大小为n+m,因为这个博主整了2发罚时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include<bits/stdc++.h>

using namespace std;

#define ll long long

int n,m;
int c[205];

map<int,bool> mp;

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>n>>m;
for(int i=0;i<n;i++)
{
cin>>c[i];
mp[c[i]]=1;
}
for(int i=0;i<m;i++)
{
cin>>c[i+n];
}

sort(c,c+m+n);

bool f=0;
for(int i=0;i<n+m;i++)
{
if(mp[c[i]])
{
if(f==1)
{
cout<<"Yes\n";
return 0;
}
else
{
f=1;
}
}
else
{
f=0;
}
}

cout<<"No\n";

return 0;
}



C. Bingo 2

纯模拟,但是注意卡常。一是要使用break;二是不用初始化,把每个点的值先填上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include<bits/stdc++.h>

using namespace std;

#define ll long long

int n,t;
int a[200005];
map<int,int> mp;

int g[2005][2005];

int ans,ans1=1e9;

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>n>>t;
for(int i=0;i<t;i++)
{
cin>>a[i];
mp[a[i]]=i+1;
}

for(int i=1;i<=n;i++)
{
bool f=1; ans=0;
for(int j=1;j<=n;j++)
{
if(!mp[n*(i-1)+j])
{
f=0;
break;
}
else
{
ans=max(mp[n*(i-1)+j],ans);
}
}
if(f==1)
{
ans1=min(ans1,ans);
}
f=1; ans=0;
for(int j=1;j<=n;j++)
{
if(!mp[n*(j-1)+i])
{
f=0;
break;
}
else
{
ans=max(mp[n*(j-1)+i],ans);
}
}
if(f==1)
{
ans1=min(ans1,ans);
}
}

bool f=1; ans=0;
for(int i=1;i<=n;i++)
{
if(!mp[n*(i-1)+i])
{
f=0;
break;
}
else
{
ans=max(mp[n*(i-1)+i],ans);
}
}
if(f==1)
{
ans1=min(ans1,ans);
}
f=1; ans=0;
for(int i=1;i<=n;i++)
{
if(!mp[n*(i-1)+n-i+1])
{
f=0;
break;
}
else
{
ans=max(mp[n*(i-1)+n-i+1],ans);
}
}
if(f==1)
{
ans1=min(ans1,ans);
}

cout<<(ans1==1e9?-1:ans1)<<"\n";;

return 0;
}


D. Intersecting Intervals

最水D题。

存一下所有开始时间和结束时间,然后按时间顺序排,注意由于区间左右皆开,所以开始要排在结束前面

然后如果遇到开始时间,ans+现有的未完区间,并且未完区间数+1,否则-1

请开long long

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include<bits/stdc++.h>

using namespace std;

#define ll long long

bool cmp(pair<ll,ll> x,pair<ll,ll> y)
{
if(x.first==y.first)
{
return x.second<y.second;
}
return x.first<y.first;
}

ll n,s,t;
vector<pair<ll,ll> > v;

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>n;

for(ll i=0;i<n;i++)
{
cin>>s>>t;
v.push_back({s,0});
v.push_back({t,1});
}
sort(v.begin(),v.end(),cmp);

ll k=0,ans=0;
for(ll i=0;i<v.size();i++)
{
if(v[i].second==0)
{
ans+=k;
k++;
}
else
{
k--;
}
}

cout<<ans<<"\n";

return 0;
}


E. Guess the Sum

为什么加了输入输出流解绑就会出现输入问题qwq,烦

参考jiangly大神的做法

(=_=) E又>F了

非常匪夷所思地使用了最短路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include<bits/stdc++.h>

using namespace std;

#define ll long long

int n,l,r;
int v;
int pre[300005];
queue<int> q;

int main()
{
// ios::sync_with_stdio(0);
// cin.tie(0),cout.tie(0);

cin>>n>>l>>r;
r++;

q.push(l);
memset(pre,-1,sizeof pre);
pre[l]=l;

while(!q.empty())
{
int x=q.front();
q.pop();

for(int i=1;i<=(1<<n);i*=2)
{
for(auto y:{x-i,x+i})
{
if(y<0||y>(1<<n))
{
continue;
}
if(pre[y]==-1)
{
pre[y]=x;
q.push(y);
}
}
if(x&i)
{
break;
}
}
}

ll ans=0;
for(int i=r;i!=l;i=pre[i])
{
int b=i,a=pre[i];
int t=1;
if(a>b)
{
swap(a,b);
t=-t;
}
cout<<"? "<<__lg(b-a)<<" " <<a/(b-a)<<"\n";

int ret;
cin>>ret;
ans=(ans+t*ret+100)%100;
}

cout<<"! "<<ans<<"\n";

return 0;
}

F. MST Query

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include<bits/stdc++.h>

using namespace std;

#define ll long long

using namespace std;

ll n,m;
ll a[400005],b[400005],c[400005];
ll par[200005];
ll vis[400005];
ll dp[400005];

ll find(ll x)
{
return par[x]==x?x:par[x]=find(par[x]);
}

void merge(ll x,ll y)
{
x=find(x),y=find(y);
par[x]=y;
}

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>n>>m;
for(ll i=1;i<n+m;i++)
{
cin>>a[i]>>b[i]>>c[i];
}

for(ll l=1;l<=10;l++)
{
for(ll i=1;i<=n;i++)
{
par[i]=i;
}
for(ll i=1;i<n+m;i++)
{
if(c[i]>l)
{
continue;
}
if(find(a[i])!=find(b[i]))
{
merge(a[i],b[i]);
if(!vis[i])
{
dp[i]+=l;
vis[i]=1;
}
}
else if(vis[i])
{
dp[i]-=l;
vis[i]=0;
}
}
}

ll ans=0;
for(ll i=1;i<n+m;i++)
{
ans+=dp[i];
if(i>=n)
{
cout<<ans<<"\n";
}
}

return 0;
}

G. Baseball

AtCoder Beginner Contest 352 赛后总结

比赛链接

非常难绷地交了6发罚时。。。

题解

A. AtCoder Line

应该没什么好讲的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<bits/stdc++.h>

using namespace std;

int n;
int x,y,z;

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>n>>x>>y>>z;
if(x<y)
{
swap(x,y);
}
if(x>=z&&y<=z)
{
cout<<"Yes\n";
}
else
{
cout<<"No\n";
}

return 0;
}


B. Typing

又是双指针,典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<bits/stdc++.h>

using namespace std;

string s,t;

int main()
{

ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>s>>t;

for(int i=0,j=0;i<t.size();)
{
while(t[i]!=s[j]&&i<t.size())
{
i++;
}
if(i==t.size())
{
break;
}
cout<<i+1<<" ";
i++,j++;
}
cout<<"\n";

return 0;
}


C. Standing On The Shoulders

由于头高一定比肩高高,而肩高综合是一定的,因此只需要按照头高和肩高之差拍序,找到差最大的即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include<bits/stdc++.h>

using namespace std;

long long n;
struct node
{
int a,b,d;
} x[200005];

bool cmp(node a,node b)
{
return a.d>b.d;
}

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>n;
for(long long i=0;i<n;i++)
{
cin>>x[i].a>>x[i].b;
x[i].d=x[i].b-x[i].a;
}
sort(x,x+n,cmp);
long long ans=0;
for(long long i=1;i<n;i++)
{
ans+=x[i].a;
}
ans+=x[0].b;

cout<<ans<<"\n";

return 0;
}


D. Permutation Subsequence

主要是赛时非常抽线地想到了滑动窗口并立刻放弃,转头使用了ST表。

Part1. ST表

说实话,当我看到还有人用线段树时,我意识到我不是一个人。 思路其实很简单,存一下每个元素的索引。答案即是,每连续 k 个元素中最大索引与最小索引之差的最小值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include<bits/stdc++.h>

using namespace std;

int n,k,p;
int id[200005];
int ans=1e9;

int f1[200005][20],f2[200005][20];

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>p;
id[p]=i;
}

//ST表板子部分
for(int i=1;i<=n;i++)
{
f1[i][0]=f2[i][0]=id[i];
}
for(int j=1;(1<<j)<=n;j++)
{
for(int i=1;i+(1<<(j-1))<=n;i++)
{
f1[i][j]=max(f1[i][j-1],f1[i+(1<<(j-1))][j-1]);
f2[i][j]=min(f2[i][j-1],f2[i+(1<<(j-1))][j-1]);
}
}

int s=log2(k);
for(int i=1;i+k-1<=n;i++)
{
ans=min(ans,max(f1[i][s],f1[i+k-1-(1<<s)+1][s])-min(f2[i][s],f2[i+k-1-(1<<s)+1][s]));
}

cout<<ans<<"\n";

return 0;
}

跑到了49ms,过肯定是能过的

Part.2 滑动窗口

其实思路是一样的,但是用单调队列来维护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include<bits/stdc++.h>

using namespace std;

int n,k,p;
int id[200005];
int ans=1e9;

deque<int> mn,mx;

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>p;
id[p]=i;
}

//滑动窗口板子部分
for(int i=1;i<=n;i++)
{
//超过k个值了所以这个元素可以丢掉了
while(!mn.empty()&&i-mn.front()>=k)
{
mn.pop_front();
}
while(!mx.empty()&&i-mx.front()>=k)
{
mx.pop_front();
}

//如果放进来的值超过了原来的极值,把原来的极值扔掉
while(!mn.empty()&&id[i]<=id[mn.back()])
{
mn.pop_back();
}
while(!mx.empty()&&id[i]>=id[mx.back()])
{
mx.pop_back();
}
//直到不超过原来的极值,把它放在最后面维持单调性
mn.push_back(i);
mx.push_back(i);

if(i>=k)
{
ans=min(ans,id[mx.front()]-id[mn.front()]);
}
}

cout<<ans<<"\n";

return 0;
}

跑到了14ms的绝佳成绩

但其实我觉得这两种方法都可以算正解吧(?

它们的板子都是黄题。


To Be Continued…

AtCoder Beginner Contest 350 赛后总结

比赛链接

RT。这十分恶毒的Microsoft Defender在我使用美丽的Dev-c++和Code::Blocks时疯狂地弹弹窗让我扫描病毒威胁,又由于洛谷在线IDE的格式非常不自动化(指tab要自己tab)因此是我罚时+=5*5min,(╯▔皿▔)╯。

赛时没做出来的会全码写注释

题解

A. Past ABCs

依旧非常简单,不多说了放代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<bits/stdc++.h>

using namespace std;

string s;

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>s;
if(s[0]=='A'&&s[1]=='B'&&s[2]=='c')
{
int t=(s[3]-'0')*100+(s[4]-'0')*10+s[5]-'0';
if(t>=1&&t<350&&t!=316)
{
cout<<"Yes\n";
return 0;
}
}
cout<<"No\n";

return 0;
}


B. Dentist Aoki

这个题面真的好癫qwq

按题目模拟就行了没有什么思维含量,但是身为一名蒟蒻,还是交了一遍罚时,原因:没有本地运行代码导致for循环的起始写错了。其实可以用来熟悉bitset的使用(?),遂放一篇bitset的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<bits/stdc++.h>

using namespace std;

int n,q;
bitset<1005> b;

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>n>>q;
for(int i=0;i<q;i++)
{
int t;
cin>>t;
b.flip(t);
}
cout<<n-b.count()<<"\n"; //如果在一开始将bitset初始化为全1,就输出b.count()

return 0;
}

C. Sort

好了,就是这道令我罚时+=4的题力!(蠢蠢的很安心

四发都错在没有将swap了之后的id更改,谁能想到atcoder给的样例一个都查不出来(气

非常直白,存一下每个 $a_i$ 的索引,再遍历,如果 $a_i≠i$ 那么就把 $a_i$ 和 $a$ 中值为 $i$ 的元素交换,然后用vector存即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include<bits/stdc++.h>

using namespace std;

int n;
int a[200005],id[200005];
vector<pair<int,int> > s;

int main()
{
cin>>n ;
for(int i=1;i<=n;i++)
{
cin>>a[i];
id[a[i]]=i;
}
for(int i=1;i<=n;i++)
{
if(a[i]!=i)
{
swap(a[i],a[id[i]]);
s.push_back({i,id[i]});
id[a[id[i]]]=id[i];
}
}
cout<<s.size()<<"\n" ;
for(int i=0;i<s.size();i++)
{
cout<<s[i].first<<" "<<s[i].second<<"\n";
}

return 0 ;
}

D. New Friends

某位zhy同学没有开龙龙导致WA掉,让我们恭喜他

就是让每一个连通图都变成完全图,所以使用 冰茶几 并查集维护,求出把所有图变成完全图之后的总边数,再减掉原有的变数即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include<bits/stdc++.h>

using namespace std;

long long n,m;
long long par[200005],cnt[200005];
long long ans;

long long findf(long long x)
{
return (par[x]==x)?x:par[x]=findf(par[x]);
}

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>n>>m;
for(long long i=1;i<=n;i++)
{
par[i]=i;
}
for(long long i=0;i<m;i++)
{
long long a,b;
cin>>a>>b;
par[findf(a)]=findf(b);
}
for(long long i=1;i<=n;i++)
{
cnt[findf(i)]++;
}

for(long long i=1;i<=n;i++)
{
ans+=(cnt[i]-1)*cnt[i]/2;
}

cout<<ans-m<<"\n";

return 0;
}


E. Toward 0

学校刚教完概率与期望,我却不会做,是__导致我这样的

看到1e18的数据范围直接被MLE劝退,愣是没有想到其实可以用map,是__导致我这样的!

骰子只有1-6的点数,所以每次到的值都是可以写作 $⌊\frac{𝑛}{2^𝑖3^𝑗5^𝑘}⌋$ (因为 $⌊\frac{⌊\frac{n}{x}⌋}{y}⌋=⌊\frac{⌊\frac{n}{y}⌋}{x}⌋=⌊\frac{n}{xy}⌋$ )

所以我们就可以聪明地使用map数组dp,得到以下递推公式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include<bits/stdc++.h>

using namespace std;

long long n,a,x,y;
map<long long,double> dp;

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>n>>a>>x>>y;
dp[0]=0;
for(long long i=1;i<=n;i*=2)
{
for(long long j=1;i*j<=n;j*=3)
{
for(long long k=1;i*j*k<=n;k*=5)
{
dp[n/(i*j*k)]=0; //先新建可能取到的dp索引对应的dp数组的位置(因为使用map)
}
}
}

for(map<long long,double>::iterator i=dp.begin();i!=dp.end();i++)
{
if(!i->first)
{
continue;
}
double p=x+dp[i->first/a],q=6.0*y; //p是选择/a的期望,q是选择抛骰子的期望
for(int j=2;j<=6;j++)
{
q+=dp[i->first/j];
}
q/=5;
i->second=min(p,q); //递推,最小代价
}

cout<<fixed<<setprecision(6)<<dp[n]<<"\n";

return 0;
}


F. Transpose

非常经典的栈,试问Atcoder,为何E>F。

其实感觉难度不是很大,不知道为什么是绿并F题,但话说我一直都很讨厌这种 (((()))(()) 的题QAQ

同样也是直接模拟,但是首先利用栈来记录这个字符串所有括号的位置和字母是否最终被反转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include<bits/stdc++.h>

using namespace std;

string s;
int p[500005]; //用于配对左括弧和右括弧的位置
stack<int> st; //用于存储左括弧

void output(int x,int y)
{
if(x<=y)
{
for(int i=x;i<=y;i++)
{
if(s[i]=='(')
{
if(p[i]!=i+1) //如果括号组内不为空
{
output(p[i]-1,i+1); //整体翻转输出
}
i=p[i]; //跳到下一组
}
else
{
cout<<s[i];
}
}
}
else
{
for(int i=x;i>=y;i--)
{
if(s[i]==')')
{
if(p[i]!=i-1)
{
output(p[i]+1,i-1); //整体翻转输出
}
i=p[i]; //跳到下一组
}
else
{
cout<<s[i];
}
}
}
}


int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>s;
for(int i=0;i<s.size();i++)
{
if(s[i]=='(')
{
st.push(i);
}
else if(s[i]==')')
{
p[st.top()]=i;
p[i]=st.top();
st.pop();
}
else
{
//如果这个字符在奇数个括号组里,就说明它会被大小写反转,偶数则会被反转回来
if(st.size()&1)
{
if(s[i]>='a'&&s[i]<='z')
{
s[i]-='a'-'A';
}
else
{
s[i]-='A'-'a';
}
}
}
}
output(0,s.size()-1);
cout<<"\n";

return 0;
}

G. Mediator

一道被建议评橙的G题(现在是蓝),Atcoder怎么总能水暴力(?

于是投喂一篇暴力(它甚至不需要吸氧就能过我真的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// Let us water a brute force ! 
#include<bits/stdc++.h>

using namespace std;

const long long mod=998244353;

long long n,q;
vector<long long> e[200005];
bool t[200005];

void geti(long long &op,long long &u,long long &v,long long xl) //如题
{
op=1+(((op*(1+xl))%mod)%2);
u=1+(((u*(1+xl))%mod)%n);
v=1+(((v*(1+xl))%mod)%n);
}

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>n>>q;
long long xl=0;

while(q--)
{
memset(t,0,sizeof t);

long long op,u,v;
cin>>op>>u>>v;

geti(op,u,v,xl);

if(op==1)
{
e[v].push_back(u);
e[u].push_back(v); //存边
continue;
}

for(long long i:e[u]) //遍历与u相邻的点
{
t[i]=1;
}

bool flag=0;
for(long long i:e[v])
{
if(t[i]) //判断是否与v相邻
{
cout<<i<<"\n";
xl=i;
flag=1;
break;
}
}
if(!flag) //否则输出0
{
cout<<0<<"\n";
xl=0;
}
}

return 0;
}

好吧下面是正解

使用启发式合并。如果两个点有一个共同的邻点,那么有三种可能:

  1. u是v的祖父
  2. v是u的祖父
  3. u和v是兄弟

所以依然使用带权并查集进行维护。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include<bits/stdc++.h>

using namespace std;

const long long mod=998244353;

long long n,q;
vector<long long> e[200005];
int f[200005],par[200005],siz[200005];
//分别为点i的父亲,点i的根,点i的深度

void geti(long long &op,long long &u,long long &v,long long xl)
{
op=1+(((op*(1+xl))%mod)%2);
u=1+(((u*(1+xl))%mod)%n);
v=1+(((v*(1+xl))%mod)%n);
}

void dfs(int x) //遍历并确定每个点的父亲节点
{
for(auto i:e[x])
{
if(i==f[x])
{
continue;
}
f[i]=x;
dfs(i);
}
}

int query(int x,int y)
{
if(f[x]==f[y]&&f[x]) //如果两点为兄弟节点
{
return f[x];
}
if(f[f[x]]==y) //x的祖父为y
{
return f[x];
}
if(f[f[y]]==x) //y的祖父为x
{
return f[y];
}
return 0;
}

int find(int x) //找根
{
return x==par[x]?x:par[x]=find(par[x]);
}

void merge(int x,int y) //合并
{
int fx=find(x),fy=find(y);
if(fx==fy)
{
return;
}
par[fy]=fx;
siz[fx]+=siz[fy];
siz[fy]=0;
}

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>n>>q;
for(int i=1;i<=n;i++) //初始化
{
par[i]=i;
siz[i]=1;
}

long long xl=0;

while(q--)
{
long long op,u,v;
cin>>op>>u>>v;

geti(op,u,v,xl);

if(op==1)
{
if(siz[find(u)]<siz[find(v)])
{
swap(u,v);
}
e[v].push_back(u);
e[u].push_back(v);
f[v]=u;
merge(u,v);
dfs(v);
continue;
}

xl=query(u,v);
cout<<xl<<"\n";
}

return 0;
}

wow写完啦!!!!!

感谢观看呀

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×