Codeforces Round 986 (Div. 2)题解记录(A~E)
不知道cf重新交通过会重新算积分,直接多扣了300积分,真难绷,引以为戒吧
比赛链接:https://codeforces.com/contest/2028
A. Alice's Adventures in "Chess"
爱丽丝正试图在乡村与红皇后会面!目前,爱丽丝位于位置\((0, 0)\),红皇后位于位置\((a, b)\)。爱丽丝只能朝四个基本方向(北、东、南、西)移动。
更正式地说,如果爱丽丝在点\((x, y)\),她将执行以下操作之一:
- 向北移动(用N表示),移动到\((x, y+1)\);
- 向东移动(用E表示),移动到\((x+1, y)\);
- 向南移动(用S表示),移动到\((x, y-1)\);或者
- 向西移动(用W表示),移动到\((x-1, y)\)。
爱丽丝的移动是预先确定的。她有一个字符串\(s\),代表她从左到右执行的一系列移动。一旦她到达序列的末尾,她将永远重复相同的移动模式。
你能帮爱丽丝计算出她是否最终会与红皇后相遇吗?
\(Input\)
每个测试包含多个测试用例。第一行包含测试用例的数量\(t\)(\(1 \le t \le 500\))。测试用例的描述随后给出。
每个测试用例的第一行包含三个整数\(n\),\(a\),\(b\)(\(1 \le n\),\(a\),\(b \le 10\))——字符串的长度和红皇后的初始坐标。
第二行包含一个长度为\(n\)的字符串\(s\),只由字符N,E,S或W组成。
\(Output\)
对于每个测试用例,输出一个字符串“YES”或“NO”(不带引号),表示爱丽丝最终是否会与红皇后相遇。
\(Sample\)
6
2 2 2
NE
3 2 2
NNE
6 2 1
NNEESW
6 10 10
NNEESW
3 4 2
NEE
4 5 5
NEWS
YES
NO
YES
YES
YES
NO
思路:她将永远重复相同的移动模式。这句话很重要,爱丽丝在一次循环中不一定会遇到红皇后,显然最坏每次循环后最坏移动0或1,因为数据范围很小直接重复循环1000次(多次)就好,看看是否遇到就好,这里脑抽,赛后一个多小时又交了一发
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
int main()
{
fio();
ll t;
cin>>t;
while(t--)
{
ll n,a,b;
cin>>n>>a>>b;
string f;
cin>>f;
ll cnt,ans;
cnt=ans=0;
ll pd=0;
ll i=0;
ll gs=4000;
while(1)
{
if(cnt==a&&ans==b)
{
pd=1;
break;
}
gs--;
if(gs==0)
break;
if(f[i]=='N')
{
ans++;
}
else if(f[i]=='S')
{
ans--;
}
else if(f[i]=='E')
{
cnt++;
}
else if(f[i]=='W')
cnt--;
i++;
if(i==n)
{
i=0;
}
}
if(pd)
cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}
B. Alice's Adventures in Permuting
爱丽丝把“变换”和“排列”这两个词搞混了!她有一个由三个整数\(n\),\(b\),\(c\)定义的数组\(a\):数组\(a\)的长度为\(n\),由\(a_i = b \cdot (i - 1) + c\)给出,对于\(1 \le i \le n\)。例如,如果\(n=3\),\(b=2\),\(c=1\),那么\(a=[2 \cdot 0 + 1, 2 \cdot 1 + 1, 2 \cdot 2 + 1] = [1, 3, 5]\)。
现在,爱丽丝非常喜欢\([0, \ldots, n-1]\)的排列\(^{\text{∗}}\),并且希望将\(a\)转换成一个排列。在一次操作中,爱丽丝用\(a\)的\(\operatorname{MEX}\)\(^{\text{†}}\)替换\(a\)中的最大元素。如果\(a\)中有多个最大元素,爱丽丝选择最左边的一个进行替换。
你能帮爱丽丝计算出她需要进行多少次操作才能使\(a\)首次变成一个排列吗?如果不可能,你应该报告。
\(^{\text{∗}}\)长度为\(n\)的排列是一个由\(0\)到\(n-1\)的\(n\)个不同整数组成的数组,顺序任意。请注意,这与排列的常规定义略有不同。例如,\([1,2,0,4,3]\)是一个排列,但\([0,1,1]\)不是排列(\(1\)在数组中出现了两次),\([0,2,3]\)也不是排列(\(n=3\)但数组中有\(3\))。
\(^{\text{†}}\)一个数组的\(\operatorname{MEX}\)是不属于该数组的最小的非负整数。例如,\([0, 3, 1, 3]\)的\(\operatorname{MEX}\)是\(2\),\([5]\)的\(\operatorname{MEX}\)是\(0\)。
\(Input\)
每个测试包含多个测试用例。第一行包含测试用例的数量\(t\)(\(1 \le t \le 10^5\))。随后是测试用例的描述。
每个测试用例只有一行,包含三个整数\(n\),\(b\),\(c\)(\(1\le n\le 10^{18}\);\(0\le b\),\(c\le 10^{18}\))——数组的参数。
\(Output\)
对于每个测试用例,如果数组永远无法变成一个排列,输出\(-1\)。否则,输出使数组变成排列的最小操作次数。
\(Sample\)
7
10 1 0
1 2 3
100 2 1
3 0 1
3 0 0
1000000000000000000 0 0
1000000000000000000 1000000000000000000 1000000000000000000
0
1
50
2
-1
-1
1000000000000000000
思路:给的序列其实是个以c为初项,以b为公差的等差数列,
1.首先思考下不可能条件吧:如果对于目前最右边的最大数,每次变换后仍然是他最大就是不可能情况,对于一个公差不为0的等差数列,显然是不会出现这种情况的
所以思考公差为0的情况,枚举下初项为1,4可发现,如果c+1<n-1一定无解
2.有解次数,如何最小化?
其实只要看在这个序列中多个已经符合n排列的数的种类就行了,
如n=4,b=2,c=0时排列一开始为0 2 4 6,显然,对于每个不符合的数每次会变成符合的数,而且符合的数他是不用变的(当只有一个时)
所以当b>0时,直接根据公式b*i+c<=n-1,求出i(c>n-1时,这里会得出i<0),否则i=0,然后如果c<=n-1,则i++,否则i不变,最后和n取个min就得出答案了
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
int main()
{
fio();
ll t;
cin>>t;
while(t--)
{
ll n,b,c;
cin>>n>>b>>c;
if(b==0&&c+1<n-1)
{
cout<<-1<<endl;
continue;
}
else
{
ll gs=0;
ll x=n-1;
if(b>0)
gs=(x-c)/b;
if(c<=n-1)
gs++;
cout<<min(n-gs,(ll)n)<<endl;
}
}
}
C. Alice's Adventures in Cutting Cake
爱丽丝在疯帽子的茶会上!有一个由\(n\)节组成的长蛋糕,每节的美味值分别为\(a_1, a_2, \ldots, a_n\)。茶会上有\(m\)个生物,不包括爱丽丝。
爱丽丝将把蛋糕切成\(m + 1\)块。形式上,她将蛋糕分成\(m + 1\)个子数组,每个子数组由一些相邻的部分构成。一块蛋糕的美味度是其各节美味度的总和。之后,她将这\(m + 1\)块蛋糕分给\(m\)个生物和她自己(她的那块可以是空的)。然而,每个\(m\)个生物只有在其蛋糕块的美味度至少为\(v\)时才会感到满意。
爱丽丝希望确保每个生物都满意。在这一条件下,她还希望最大化她自己那块蛋糕的美味度。你能帮爱丽丝找到她那块蛋糕的最大美味度吗?如果没有办法确保每个生物都满意,请输出\(-1\)。
\(Input\)
每个测试包含多个测试用例。第一行包含测试用例的数量\(t\)(\(1 \le t \le 10^4\))。随后是测试用例的描述。
每个测试用例的第一行包含三个整数\(n, m, v\)(\(1\le m\le n\le 2\cdot 10^5\);\(1\le v\le 10^9\))——蛋糕的节数、生物的数量以及每个生物对美味度的最小要求。
下一行包含\(n\)个空格分隔的整数\(a_1, a_2, \ldots, a_n\)(\(1 \le a_i \le 10^9\))——各节蛋糕的美味度。
所有测试用例中\(n\)的总和不超过\(2\cdot 10^5\)。
\(Output\)
对于每个测试用例,输出爱丽丝能够获得的最大美味度,或者如果没有办法确保每个生物都满意,则输出\(-1\)。
\(Sample\)
7
6 2 1
1 1 10 1 1 10
6 2 2
1 1 10 1 1 10
6 2 3
1 1 10 1 1 10
6 2 10
1 1 10 1 1 10
6 2 11
1 1 10 1 1 10
6 2 12
1 1 10 1 1 10
6 2 12
1 1 1 1 10 10
22
12
2
2
2
0
-1
思路:我们一开始肯定无法直接知道从什么点开始到什么点结束为爱丽丝能拿的最大美味度,所以既然我不知道,那我每个都试试呗
首先用前缀和后缀数组wz1,wz2处理好到某个点能有多少个人被分配好蛋糕,还要用另一个前缀数组pre处理好前缀和,随后将后缀数组转成正序数组,然后每到i就知道已经分好了wz1[i-1]个人,所以还要分m-wz1[i-1]个人,直接二分(lower_bound)那个正序数组找到第一个符合的下标i1,然后n-i1,(记得二分包含0位置哦),即可得到爱丽丝在符合题意状况下能拿到的区间蛋糕(pre[n-i1]-pre[i-1]),在所有可能中取个最大值就好
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll pre[250000];
ll sub[250000];
ll wz1[250000];
ll wz2[250000];
ll a[250000];
ll ok[250000];
int main()
{
fio();
ll t;
cin>>t;
while(t--)
{
ll n,m,v;
cin>>n>>m>>v;
for(ll i=1;i<=n;i++)cin>>a[i],sub[i]=0,pre[i]=0,wz1[i]=0,wz2[i]=0;
sub[n+1]=0;
ll sum=0;
for(ll i=1;i<=n;i++)
{
sum+=a[i];
pre[i]=pre[i-1]+a[i];
wz1[i]=max(wz1[i],wz1[i-1]);
if(sum>=v)
{
sum=0;
wz1[i]++;
}
}
sum=0;
wz2[n+1]=0;
for(ll i=n;i>=1;i--)
{
sum+=a[i];
wz2[i]=max(wz2[i],wz2[i+1]);
if(sum>=v)
{
wz2[i]++;
sum=0;
}
ok[n-i+1]=wz2[i];
}
// cout<<wz1[n]<<endl;
if(wz1[n]<=m-1)
{
cout<<-1<<endl;
}
else
{
ll ans=0;
for(ll i=1;i<=n;i++)
{
ll x=wz1[i-1];
ll u=m-x;
ll op=lower_bound(ok,ok+1+n,u)-ok;
ans=max(ans,pre[n-op]-pre[i-1]);
}
cout<<ans<<endl;
}
}
}
D. Alice's Adventures in Cards
爱丽丝正在和红心皇后、红心国王以及红心杰克玩牌。在他们的牌游戏中有\(n\)种不同类型的牌。爱丽丝目前有一张类型为\(1\)的牌,并且需要一张类型为\(n\)的牌才能逃离仙境。其他玩家每人都有每种类型的一张牌。
在这个牌游戏中,爱丽丝可以和另外三个玩家交换牌。每个玩家对\(n\)种类型的牌有不同的偏好,这可以通过排列\(q\)、\(k\)和\(j\)来描述,分别对应皇后、国王和杰克的偏好。
如果对于玩家的排列\(p\),\(p_a > p_b\),那么这个玩家更看重牌\(a\)而不是牌\(b\)。然后,这个玩家愿意用牌\(b\)和爱丽丝交换牌\(a\)。爱丽丝的偏好很直接:如果\(a > b\),她就更看重牌\(a\)而不是牌\(b\),而且她也只会根据这些偏好进行交易。
确定爱丽丝是否可以根据这些偏好,从类型为\(1\)的牌交易到类型为\(n\)的牌,如果可能的话,请给出一组可能的交易方式。
\(^{\text{∗}}\)长度为\(n\)的排列是一个由\(n\)个不同的整数组成的数组,这些整数从\(1\)到\(n\),顺序任意。例如,\([2,3,1,5,4]\)是一个排列,但\([1,2,2]\)不是排列(\(2\)出现了两次),而且\([1,3,4]\)也不是排列(\(n=3\)但数组中有\(4\))。
\(Input\)
每个测试包含多个测试用例。第一行包含测试用例的数量\(t\)(\(1 \le t \le 10^4\))。
每个测试用例的第一行包含一个整数\(n\)(\(2\le n\le 2\cdot 10^5\))——牌的类型数量。
接下来的三行包含皇后、国王和杰克的偏好。这些行每行包含\(n\)个整数\(p_1, p_2, \ldots, p_n\)(\(1\le p_i\le n\))——对应玩家偏好的排列。
所有测试用例中\(n\)的总和不超过\(2\cdot 10^5\)。
\(Output\)
对于每个测试用例,在第一行输出一个字符串"YES"或"NO"(不包括引号),表示爱丽丝是否可以交易到牌\(n\)。
如果第一行是"YES",那么在下一行输出\(k\)——爱丽丝将进行的交易次数。在接下来的\(k\)行中,输出空格分隔的一个字符\(c\in \{\texttt{q}, \texttt{k}, \texttt{j}\}\)和一个整数\(x\),表示爱丽丝与玩家\(c\)交易以获得牌\(x\)。必须满足在第\(k\)行,\(x = n\)。如果有多个解决方案,请打印任何一个。
你可以以任何情况(大写或小写)输出这个答案。例如,字符串"yEs"、"yes"、"Yes"和"YES"将被视为肯定的回应。同样,表示交易中玩家的字符\(c\)(\(\texttt{Q}, \texttt{K}, \texttt{J}\)将与它们的小写变体一起被接受)。
\(Sample\)
2
3
1 3 2
2 1 3
1 2 3
4
2 3 1 4
1 2 3 4
1 4 2 3
YES
2
k 2
q 3
NO
错误版思路:这个大家不要看,直接去看下面正确版的,这里留着是为了警示自己以后要留心点,其实记忆化递归卡过去是侥幸的,当然现在这个被hack了。由答案可知,我得输出过程,所以一定是dfs或者bfs,思考下bfs,不好写,因为过程会有问题(数据储存量会太大),所以就dfs了,一开始想建边,但是明显\(n^2\)的时间复杂度,所以否决,然后想了下题目给了两个约束,即优先度,和爱丽丝只能换更大的数,显然如果我dfs,一个数搜过了,之后一定不会搜了,所以准备记忆化+dfs,然后在想的时候优化了下遍历时间,一开始用3个set根据权值(优先度)对于q,k,j的手牌进行排序,随后开了两个二维数组,一个记录对应数的位置,还有一个记录排序后的数随后直接dfs+记忆化(这里用的是两个二维数组)就过了,但是时间是1468ms,有点危险,看了下样例全是NO才是1468ms,其他都是<900ms就过了,所以如果要优化下,看下能否特判不行情况吧,这里也脑抽重新交了一发,积分--,具体时间复杂度自己也不清楚
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
set<pair<ll,ll>>q[4];
bool vis[250000];
vector<pair<char,ll>>ans;
ll pd=0;
ll n;
ll a[4][250000];
ll b[4][250000];
void dfs(ll x)
{
vis[x]=1;
if(x==n)
{
pd=1;
return ;
}
ll wz;
wz=a[1][x];
// ll op=0;
for(ll i=wz+1;i<=n;i++)
{
if(b[1][i]>x&&vis[b[1][i]]==0)
{
ans.push_back({'q',b[1][i]});
dfs(b[1][i]);
if(pd)
return ;
ans.pop_back();
}
}
wz=a[2][x];
for(ll i=wz+1;i<=n;i++)
{
if(b[2][i]>x&&vis[b[2][i]]==0)
{
ans.push_back({'k',b[2][i]});
dfs(b[2][i]);
if(pd)
return ;
ans.pop_back();
}
}
wz=a[3][x];
for(ll i=wz+1;i<=n;i++)
{
if(b[3][i]>x&&vis[b[3][i]]==0)
{
ans.push_back({'j',b[3][i]});
dfs(b[3][i]);
if(pd)return ;
ans.pop_back();
}
}
}
int main()
{
fio();
ll t;
cin>>t;
while(t--)
{
pd=0;
cin>>n;
ans.clear();
q[1].clear();
q[2].clear();
q[3].clear();
for(ll i=1;i<=3;i++)
{
for(ll j=1;j<=n;j++)
{
ll w;
cin>>w;
vis[w]=0;
q[i].insert({n-w+1,j});
}
ll cnt=0;
for(auto j:q[i])
{
cnt++;
a[i][j.second]=cnt;
b[i][cnt]=j.second;
}
}
dfs(1);
if(pd==0)
{
cout<<"NO"<<endl;
}
else
{
cout<<"YES"<<endl;
cout<<ans.size()<<endl;
for(auto j:ans)
{
cout<<j.first<<" "<<j.second<<endl;
}
}
}
}
正确思路:第一个版本被hack了,以为加个位置记忆可以过,忽略了他有变大约束,这样dfs始终都是没办法的,最终一定可以被hack的,因为总能构造出吃满时间的样例。其实这里用bfs时间复杂度更好直接\(3*n\),但是注意Alice约束怎么优化?直接每次以最小的点走一遍即可!这样子位置就可以记忆化了(因为走过了后续再走到被标记过的点一定是有任何没用的,如果记忆了,就直接break,时间复杂度为\(3*n*logn\).但是如何找答案路线?确实bfs不适合找路线(不然内存会爆),所以就dfs呗,在bfs的过程中,我直接在目前点和下个可行点建边,然后这样子最多n-1跳边,然后记忆化走一遍不就n的时间复杂度了,在走的过程中边存储答案就可以了,所以最后时间复杂度\(3*n*logn+n\),dfs+bfs巧妙结合,即可,时间复杂度687ms,这里如果用加速流+int就会624ms,但是如果加速流+long long就会1609ms,有点奇怪。
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
// void fio()
// {
// ios::sync_with_stdio(0);
// cin.tie(0);
// cout.tie(0);
// }
inline ll read()
{
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
x=x*10+ch-'0',ch=getchar();
return x*f;
}
set<pair<ll,ll>>q[4];
bool vis[250000];
vector<pair<char,ll>>ans;
vector<pair<char,ll>>g[250000];
ll pd=0;
ll n;
ll a[4][250000];
ll b[4][250000];
bool vi[4][250000];
void bfs(ll x)
{
priority_queue<ll,vector<ll>,greater<ll>>q;
q.push(x);
vis[x]=1;
while(!q.empty())
{
ll x=q.top();
q.pop();
for(ll i=a[1][x];i<=n;i++)
{
if(vi[1][i])
break;
if(vis[b[1][i]]==0&&b[1][i]>x)
{
g[x].push_back({'q',b[1][i]});
if(b[1][i]==n)
{
pd=1;
break;
}
vis[b[1][i]]=1;
q.push(b[1][i]);
}
vi[1][i]=1;
}
for(ll i=a[2][x];i<=n;i++)
{
if(vi[2][i])
break;
if(vis[b[2][i]]==0&&b[2][i]>x)
{
g[x].push_back({'k',b[2][i]});
if(b[2][i]==n)
{
pd=1;
break;
}
vis[b[2][i]]=1;
q.push(b[2][i]);
}
vi[2][i]=1;
}
for(ll i=a[3][x];i<=n;i++)
{
if(vi[3][i])
break;
if(vis[b[3][i]]==0&&b[3][i]>x)
{
g[x].push_back({'j',b[3][i]});
if(b[3][i]==n)
{
pd=1;
break;
}
vis[b[3][i]]=1;
q.push(b[3][i]);
}
vi[3][i]=1;
}
}
}
void dfs(ll x)
{
vis[x]=1;
if(x==n)
{
pd=1;
return ;
}
for(auto j:g[x])
{
if(vis[j.second]==0)
{
ans.push_back(j);
dfs(j.second);
if(pd)
return ;
ans.pop_back();
}
}
}
int main()
{
// fio();
ll t;
t=read();
while(t--)
{
pd=0;
n=read();
ans.clear();
q[1].clear();
q[2].clear();
q[3].clear();
for(ll i=1;i<=3;i++)
{
for(ll j=1;j<=n;j++)
{
ll w;
g[j].clear();
w=read();
vis[w]=0;
vi[1][j]=vi[2][j]=vi[3][j]=0;
q[i].insert({n-w+1,j});
}
ll cnt=0;
for(auto j:q[i])
{
cnt++;
a[i][j.second]=cnt;
b[i][cnt]=j.second;
// cout<<j.second<<" ";
}
//cout<<endl;
}
bfs(1);
if(pd==0)
{
cout<<"NO"<<endl;
}
else
{
pd=0;
for(ll i=1;i<=n;i++)vis[i]=0;
//cout<<g[1][0].second<<endl;
dfs(1);
cout<<"YES"<<endl;
cout<<ans.size()<<endl;
for(auto j:ans)
{
cout<<j.first<<" "<<j.second<<endl;
}
}
}
}
E. Alice's Adventures in the Rabbit Hole
Alice 在兔子洞的底部!兔子洞可以被建模为一棵树\(^{\text{∗}}\),它在顶点 \(1\) 处有一个出口,Alice 从某个顶点 \(v\) 开始。她想逃出洞口,但不幸的是,红心皇后下令处决她。
每一分钟,都会抛一枚公平的硬币。如果硬币正面朝上,Alice 可以移动到她当前位置的相邻顶点;否则,红心皇后会将 Alice 拉到皇后选择的相邻顶点。如果 Alice 最终出现在树的任何非根叶子上\(^{\text{†}}\),Alice 就输了。
假设他们俩都以最优策略行动,计算 Alice 从每个起始顶点 \(1\le v\le n\) 成功逃脱的概率。由于这些概率可能非常小,以 \(998\,244\,353\) 为模输出它们。
正式地说,设 \(M = 998\,244\,353\)。可以证明,确切的答案可以表示为一个不可约分数 \(\frac{p}{q}\),其中 \(p\) 和 \(q\) 是整数,且 \(q \not \equiv 0 \pmod{M}\)。输出等于 \(p \cdot q^{-1} \bmod M\) 的整数。换句话说,输出这样一个整数 \(x\),使得 \(0 \le x < M\) 且 \(x \cdot q \equiv p \pmod{M}\)。
\(^{\text{∗}}\) 树是一个有 \(n\) 个顶点和 \(n-1\) 条边的连通简单图。
\(^{\text{†}}\) 叶子是一个只连接到一条边上的顶点。
\(Input\)
每个测试包含多个测试案例。第一行包含测试案例的数量 \(t\)(\(1 \le t \le 10^4\))。每个测试案例的描述随后给出。
每个测试案例的第一行包含一个整数 \(n\)(\(2\le n\le 2\cdot 10^5\))——树中的顶点数。
接下来的 \(n - 1\) 行中,第 \(i\) 行包含两个整数 \(x_i\) 和 \(y_i\)(\(1 \le x_i, y_i \le n\) 且 \(x_i \neq y_i\))——树的边。保证给出的边构成一棵树。
保证所有测试案例中 \(n\) 的总和不超过 \(2\cdot 10^5\)。
\(Output\)
对于每个测试案例,输出一行包含 \(n\) 个整数——从顶点 \(1, 2, \ldots, n\) 开始 Alice 逃脱的概率。由于这些概率可能非常小,以 \(998,244,353\) 为模输出它们。
\(Sample\)
2
5
1 2
1 3
2 4
3 5
9
1 2
2 3
4 5
5 6
7 8
8 9
2 4
5 7
1 499122177 499122177 0 0
1 499122177 0 332748118 166374059 0 443664157 720954255 0
样例解析:
Alice 从根(顶点 \(1\))逃脱的概率是 \(1\),这是根据定义的。
Alice 会立即在顶点 \(4\) 和 \(5\) 输掉,因为它们是叶子节点。
从其他两个顶点开始,Alice 逃脱的概率是 \(\frac{1}{2}\),因为红心皇后会将她拉向叶子节点。
思路:一开始的思路错了,上水课时画了下图,发现红皇后最好策略应该是往离她最近的子树的叶子上走,爱丽丝就是往1走最好,然后推出了个错误式子,设dis[x]为x到以x为根的子树的叶子的最短距离,父亲节点的成功率为p,于是错误式子得出\(p*(\frac{1}{2}+\frac{1}{2}*\frac{1}{dis[x]+1})\),这样总样例过了,然后wa2,随后还以为是直接找到最近叶子就好了,然后bfs了一遍,还是错在一样的点,遂看了下题解。其实思路从dis[x]出发是对的,但是究竟公式是什么?答案是\(p*\frac{dis[x]}{dis[x]+1}\),所以我只好认为x到离他最近的叶子节点路上在许多可能性中,只有\(\frac{1}{dis[x]+1}\)可能会到达叶子节点,好了,原理写完了,说下怎么实现,首先dfs一遍回溯传递最近的叶子节点距离,然后再dfs一遍,这次是算答案,每次要传递当前点的可能到下个点(特判1),最后答案得出
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
// inline ll read()
// {
// ll x=0,f=1;
// char ch=getchar();
// while(ch<'0'||ch>'9')
// {
// if(ch=='-')
// f=-1;
// ch=getchar();
// }
// while(ch>='0' && ch<='9')
// x=x*10+ch-'0',ch=getchar();
// return x*f;
// }
vector<ll>g[250000];
ll dis[250000];
ll z[250000];
ll dfs(ll x,ll fa,ll sd)
{
ll u=9999999999;
for(auto j:g[x])
{
if(j!=fa)
{
u=min(u,dfs(j,x,sd+1));
dis[x]=min(dis[x],u-sd);
}
}
if(x!=1&&g[x].size()==1)
{
u=sd;
dis[x]=min(dis[x],(ll)0);
}
return u;
}
void df(ll x,ll fa,ll v,ll sd)
{
if(x==1)
{
z[x]=v=1;
}
else if(dis[x]==0)
{
z[x]=v=0;
}
else
{
ll u=ksm(dis[x]+1,mod-2);
z[x]=v=(v%mod*((dis[x]%mod*u%mod)%mod)%mod)%mod%mod;
}
for(auto j:g[x])
{
if(j!=fa)
{
df(j,x,v,sd+1);
}
}
}
int main()
{
fio();
ll t;
cin>>t;
while(t--)
{
ll n;
cin>>n;
for(ll i=1;i<=n;i++)g[i].clear(),dis[i]=9999999999;
for(ll i=1;i<n;i++)
{
ll l,r;
cin>>l>>r;
g[l].push_back(r);
g[r].push_back(l);
}
ll j=dfs(1,0,1);
df(1,0,1,1);
for(ll i=1;i<=n;i++)
cout<<z[i]<<" ";
cout<<endl;
}
}