A LCT
题意:给出n-1条边,每条边后面紧随一个提问,求以c为根的树的最长路径
思路:由于题目给了空间限制,不能把所有的树都存下来去找,因此我们需要一个空间复杂度O(1)的解法,这边可以考虑采用带权并查集去解决这个问题,具体实现思路由代码以及注释给出。
代码:
# include <iostream>
# include <string>
# include <cstring>
# include <map>
# include <queue>
# include <algorithm>
# include <cmath>
# include <vector>
using namespace std;
# define ll long long
const ll N = 1e6 + 10 ;
int fa[ N] = { 0 } ;
int dep[ N] = { 0 } ;
int lon[ N] = { 0 } ;
int find ( int x)
{
if ( fa[ x] != x)
{
lon[ fa[ x] ] = max ( lon[ fa[ x] ] , lon[ x] + dep[ x] ) ;
int f = find ( fa[ x] ) ;
dep[ x] += dep[ fa[ x] ] ;
fa[ x] = f;
}
return fa[ x] ;
}
void solve ( )
{
ll n;
cin >> n;
for ( int i = 1 ; i <= n; i++ )
{
fa[ i] = i;
dep[ i] = 0 ;
lon[ i] = 1 ;
}
for ( int i = 1 ; i < n; i++ )
{
int a, b, c;
cin >> a >> b >> c;
fa[ b] = a;
dep[ b] = 1 ;
int p = find ( b) ;
cout << lon[ c] - 1 ;
if ( i == n - 1 )
{
cout << endl;
}
else
{
cout << ' ' ;
}
}
}
signed main ( )
{
ios:: sync_with_stdio ( 0 ) , cin. tie ( 0 ) , cout. tie ( 0 ) ;
ll t;
cin >> t;
while ( t-- )
{
solve ( ) ;
}
}
C sort 4
题意:给出一个大小为n的乱序数组,每次选4个数进行随机排序,直到变成你想要的状态停止,这样算一次操作,问最少几次操作可以使得数组变为有序的。
思路:首先我们发现,如果按照乱序数组一个个找对应位置,会找到许多大小不1的环,那么我们可以根据环的大小划分如何操作,对于大小为1的,就是在对应位置上的,明显不用操作,否则打乱了麻烦,对于大小为2的,我们可以把2组拼凑在一起,来减小操作次数。对于大小为3,4的明显无法组合肯定要一个操作次数,至于大于等于5的环,尽可能3个3个归位,最后不足5个的判断是2还是3,4就行
代码:
# include <iostream>
# include <string>
# include <cstring>
# include <map>
# include <queue>
# include <algorithm>
# include <cmath>
# include <vector>
using namespace std;
# define ll long long
const ll N = 1e6 + 10 ;
int f[ N] = { 0 } ;
int arr[ N] = { 0 } ;
void solve ( )
{
ll n;
cin >> n;
for ( int i = 1 ; i <= n; i++ )
{
f[ i] = 0 ;
cin >> arr[ i] ;
}
int ans = 0 ;
int sum2 = 0 ;
for ( int i = 1 ; i <= n; i++ )
{
ll now = arr[ i] ;
ll shu = 0 ;
while ( ! f[ now] )
{
f[ now] = 1 ;
now = arr[ now] ;
shu++ ;
}
if ( shu == 2 )
{
sum2++ ;
}
else if ( shu == 3 || shu == 4 )
{
ans++ ;
}
else if ( shu>= 5 )
{
while ( shu > 4 )
{
ans++ ;
shu -= 3 ;
}
if ( shu != 2 )
{
ans++ ;
}
else
{
sum2++ ;
}
}
}
ans += ( sum2 + 1 ) / 2 ;
cout << ans << endl;
}
signed main ( )
{
ios:: sync_with_stdio ( 0 ) , cin. tie ( 0 ) , cout. tie ( 0 ) ;
ll t;
cin >> t;
while ( t-- )
{
solve ( ) ;
}
}
F Good Tree
题意,给出一个x,问最小需要几个结点构成树使得树上存在一组u,v。使得f(u)-f(v)=x,f(x)表示从x出发到任意一个结点的路径和。
思路:稍微打一点表或者手动推几组数据会发现答案是根据x增大而增大,与x成正比的,对于n个结点,我们又可以推出n个结点的最大路径和最小路径,那么在这个差值范围内的值基本都是可以取到的。这就是我们二分的判定条件,然后就是推几组的时候会发现有一些特殊情况,我们的目的就是找出这些特殊情况。我们发现如果按照正比例推出的答案是偶数的话,如果x要求是奇数,那肯定是没有办法组合出这个x的,这种情况需要特判一下。
代码:
# include <bits/stdc++.h>
using namespace std;
# define ll long long
const ll N = 1e6 + 10 ;
int f[ N] = { 0 } ;
int arr[ N] = { 0 } ;
ll check ( ll mid, ll x)
{
ll sum = 0 ;
if ( mid == 3 )
{
sum = 1 ;
}
else if ( mid % 2 )
{
ll high = ( mid - 1 ) * mid / 2 ;
ll low = ( mid / 2 + 1 ) * ( mid / 2 ) ;
sum = high - low;
}
else
{
ll high = ( mid - 1 ) * mid / 2 ;
ll low = ( mid / 2 + 1 ) * ( mid / 2 ) / 2 + ( mid / 2 ) * ( mid / 2 - 1 ) / 2 ;
sum = high - low;
}
return sum >= x;
}
void solve ( )
{
ll x;
cin >> x;
ll l = 3 ;
ll r = INT_MAX;
ll ans = INT_MAX;
while ( l <= r)
{
ll mid = ( l + r) / 2 ;
if ( check ( mid, x) ) r = mid - 1 , ans= min ( ans, mid) ;
else l = mid + 1 ;
}
if ( x % 2 && ans % 2 == 0 )
{
ans++ ;
}
cout << ans << endl;
}
signed main ( )
{
ios:: sync_with_stdio ( 0 ) , cin. tie ( 0 ) , cout. tie ( 0 ) ;
ll t;
cin >> t;
while ( t-- )
{
solve ( ) ;
}
}
G Horse Drinnks Water
题意:将军饮马问题,给出x和y轴,求最小距离
思路:签到题,按照将军饮马思路对x对称,y对称,按照公式求一下取最小值即可。
H Yet Another Origami Problem
题意:给出一个数组,你可以取一个值Ap进行操作,可以选择两种操作,让小于Ap的Ai变成2Ap-Ai,或者让大于Ap的Ai变成2Ap-Ai。求最小的极差
思路:猜猜题,或者自己找规律证明,赛时推几组猜的结论,答案是排序后相邻两个数的差的最大公约数,gcd。具体证明可以去看官方题解证明思路,讲的很好。
I Friends
题意:给出n个人,m条关系,问有几种区间l-r,使得这个区间内任意两个人都是朋友关系,朋友关系不能间接,只能直接,一个人的区间也满足条件
思路:对于任意一个区间我们考虑,如果l - r是一个满足条件的区间,那这个区间内所有的子区间肯定也是满足条件的区间。如果对于r+1来说,l到r都和r+1有朋友关系,那么l-r的右区间就可以扩大,否则就直接把l跳到r+1的位置继续去找即可。
代码:
# include <iostream>
# include <string>
# include <cstring>
# include <map>
# include <queue>
# include <algorithm>
# include <cmath>
# include <vector>
using namespace std;
# define ll long long
const ll N = 1e6 + 10 ;
void solve ( )
{
ll n, m;
cin >> n >> m;
map< pair< int , int > , int > M;
for ( int i = 1 ; i <= m; i++ )
{
ll l, r;
cin >> l >> r;
M[ { l, r} ] = 1 ;
}
ll ans = n;
ll i = 1 ;
ll j = 2 ;
while ( j <= n)
{
if ( i == j)
{
j++ ;
continue ;
}
int f = 1 ;
for ( int k = j- 1 ; k >= i; k-- )
{
if ( M[ { k, j} ] == 0 )
{
f = 0 ;
i = k + 1 ;
j-- ;
break ;
}
}
if ( f)
{
ans += j - i;
}
j++ ;
}
cout << ans << endl;
}
signed main ( )
{
ios:: sync_with_stdio ( 0 ) , cin. tie ( 0 ) , cout. tie ( 0 ) ;
ll t;
t = 1 ;
while ( t-- )
{
solve ( ) ;
}
}