M. V-Diagram
题意:给定一个长度为 \(n\) 的数组 a,满足数组的大小呈V字排列(即中间某个地方的值最小,往两边依次单调递增),求其满足还是V字排列的子数组的最大的平均值。
限制条件:\(3\le n \le 3×10^5,1\le a_i \le 10^9\)。
题解:首先我们先考虑只选取其中一边,那么很容易就注意到越边缘的值越大,取完以后平均值就越大,直到一边的数全部取完。但是根据题目要求两边都要取,那么就直接枚举另外一边取多少个然后统计最大的答案即可。时间复杂度 \(O(n)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;int main()
{ios::sync_with_stdio(0);cin.tie(0);ll tttt=1;cin>>tttt;while(tttt--){ll n;cin>>n;double ans=0;vector<ll>a(n+7);for(int i=1;i<=n;i++){cin>>a[i];}ll p;for(int i=2;i<n;i++){if(a[i]<a[i+1]&&a[i-1]>a[i]){p=i;break;}}ll lsum=0,rsum=0;for(int i=1;i<=p;i++){lsum+=a[i];}for(int i=p+1;i<=n;i++){lsum+=a[i];ans=max(ans,1.0*lsum/i);}for(int i=p;i<=n;i++){rsum+=a[i];}for(int i=p-1;i>=1;i--){rsum+=a[i];ans=max(ans,1.0*rsum/(n+1-i));}cout<<fixed<<setprecision(18)<<ans*1.0<<'\n';}return 0;
}
J. Mysterious Tree
题意:有一个结点数为 \(n\) 的树,可能是链和菊花图中的一种。你可以询问不超过 \(\lceil \frac{n}{2} \rceil+3\) 次,每次可以询问两个结点之间有没有边,然后你要判断这个树是上述两种中的哪一种。
限制条件:\(4\le n \le 1000\)。
题解:根据给定的次数限制可以猜到这样一种方法:每次询问(1,2),(3,4),(5,6),…,(n-1,n),如果n为奇数就多问一次(n-1,n),这样刚好是 \(\lceil \frac{n}{2} \rceil\) 次。如果都不存在边的话这个图肯定是个链,因为菊花图的中心和其他所有点都有边,上述过程中至少会查询到一个。如果查到了的话也可能是链或者菊花图,因此我们要在三次内辨别。
我们现在多了的信息是有两个节点 u,v 存在一条边,怎么利用上呢?注意到:如果是菊花图的话那么u和v中肯定有一个节点会是中心结点,那么这个节点会和其他的所有点相连,而其他所有点之间不存在其他的边相连。所以我们只要判断u和v是不是中心结点就行了。我们任意取另外的两个节点c1和c2,先判断c1和u有没有边。如果有的话,u就有可能是中心结点,v就不可能。再根据c2和u是否有边判断即可。如果没有的话,只有v可能是中心结点,再询问两次c1和v以及c2和v即可,以上最多三次。时间复杂度 \(O(n)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;int main()
{ios::sync_with_stdio(0);cin.tie(0);ll tttt=1;cin>>tttt;while(tttt--){ll n;cin>>n;ll u=-1,v=-1;for(int i=1;i+1<=n;i+=2){cout<<"? "<<i<<' '<<i+1<<endl;ll x;cin>>x;if(x==1){u=i;v=i+1;break;}}if(n&1){cout<<"? "<<n-1<<' '<<n<<endl;ll x;cin>>x;if(x==1){u=n-1;v=n;}}if(u==-1&&v==-1){cout<<"! 1"<<endl;continue;}ll c1=-1,c2=-1;for(int i=1;i<=n;i++){if(c1==-1&&i!=u&&i!=v)c1=i;else if(c2==-1&&i!=u&&i!=v)c2=i;}ll x;cout<<"? "<<c1<<' '<<u<<endl;cin>>x;if(x==1){cout<<"? "<<c2<<' '<<u<<endl;cin>>x;if(x==1)cout<<"! 2"<<endl;else cout<<"! 1"<<endl;}else{cout<<"? "<<c1<<' '<<v<<endl;cin>>x;if(x==1){cout<<"? "<<c2<<' '<<v<<endl;cin>>x;if(x==1)cout<<"! 2"<<endl;else cout<<"! 1"<<endl;}else cout<<"! 1"<<endl;}}return 0;
}
D. Operator Precedence
题意,构造一个长度为 \(2n\) 的数组a,要求满足 \(\sum_{i=1}^{n} a_{2i-1} a_{2i}=a_1a_{2n}\prod_{i=2}^{n} (a_{2i-2}+a_{2i-1})\ne 0\)。
限制条件:\(2\le n \le 10^5\),构造的 \(1 \le |a_i| \le 10^{10}\)。
题解:注意到右边是乘积的形式,所以每项都不能等于0.而且如果每项不几乎都是 \(\pm1\) 的话右边的乘积会特别大,不可能等于左边的求和。因此我们考虑构造一个大多数 \((a_{2i-2}+a_{2i-1})\) 都是 \(\pm1\) 的数组。考虑最简单的情况,让 \(a_{2i-2}=-1,a_{2i-1}=2\),那么就要满足 \(-a_1+2(n-1)+2a_{2n}=a_1a_{2n}(1)^{n-1}\),我们直接让 \(a_1=1\) ,带入左边解出 \(a_{2n}=2n-3\),显然没有一项会等于零,于是这样的数组符合题意。时间复杂度 \(O(n)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;int main()
{ios::sync_with_stdio(0);cin.tie(0);ll tttt=1;cin>>tttt;while(tttt--){ll n;cin>>n;vector<ll>a(2*n+7);a[1]=1;for(int i=1;i<n;i++){a[2*i]=-1;a[2*i+1]=2;}a[2*n]=2*n-3;for(int i=1;i<=2*n;i++){cout<<a[i]<<" \n"[i==2*n];}// ll now=0;// for(int i=1;i<=n;i++)// {// now+=a[2*i-1]*a[2*i];// }// cout<<now<<'\n';// now=a[1]*a[2*n];// for(int i=2;i<=n;i++)// {// now*=a[2*i-2]+a[2*i-1];// }// cout<<now<<'\n';}return 0;
}
G. Snake Move
题意,在一个 \(n×m\) 的方格里面玩贪吃蛇,你最开始的长度为 \(k\) ,每次可以上下左右移动或者保持在原地不动(同时尾巴还是会动),不能超过边界。当你的长度为2时可以交换头尾。问到所有点的最少移动次数,求它们的平方和,对 \(2^{64}\) 取模。
限制条件:\(1\le n,m \le 3000,1\le k \le min(nm,10^5)\)。
题解:如果只是一个点的话,那就是很朴素的bfs或者最短路。但是现在的区别是有了身子,身子会影响所在点的到达时间,身子上第 \(i\) 节到达的时间至少得是 \(k+1-i\) ,那就直接对这些点多考虑一个限制条件然后跑最短路就行了。时间复杂度 \(O(nm\log (nm))\)。
点击查看代码
#include<bits/stdc++.h>using namespace std;using ll=long long;ll dx[4]={-1,1,0,0};ll dy[4]={0,0,-1,1};struct node{ll x,y,dis;friend bool operator < (node a,node b){return a.dis>b.dis;}};int main(){ios::sync_with_stdio(0);cin.tie(0);ll tttt=1;//cin>>tttt;while(tttt--){ll n,m,k;cin>>n>>m>>k;priority_queue<node>q;vector<vector<ll>>ans(n+7,vector<ll>(m+7,INT_MAX));vector<vector<ll>>mp(n+7,vector<ll>(m+7,0));for(int i=1;i<=k;i++){ll x,y;cin>>x>>y;if(i==1){q.push({x,y,0});ans[x][y]=0;}else mp[x][y]=k+1-i;}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){char c;cin>>c;if(c=='#')mp[i][j]=-1;}}while(!q.empty()){auto [x,y,dis]=q.top();q.pop();if(dis!=ans[x][y])continue;for(int i=0;i<4;i++){ll xx=x+dx[i],yy=y+dy[i];if(xx<1||xx>n||yy<1||yy>m||mp[xx][yy]==-1)continue;if(mp[xx][yy]<dis+1){if(ans[xx][yy]>dis+1){ans[xx][yy]=dis+1;q.push({xx,yy,dis+1});}}else if(ans[xx][yy]>mp[xx][yy]){ans[xx][yy]=mp[xx][yy];q.push({xx,yy,mp[xx][yy]});}}}unsigned long long res=0;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(ans[i][j]!=INT_MAX)res+=ans[i][j]*ans[i][j];//cout<<ans[i][j]<<" \n"[j==m];}}cout<<res<<'\n';}return 0;}
H. Sugar Sweet II
题意:糖果是甜的!有 \(n\) 个孩子,每个孩子有 \(a_i\) 块糖。有 \(n\) 个事件按随机顺序发生。第 \(i\) 次事件为若 \(a_i<a_{b_i}\) ,则给 \(a_i\) 加上 \(w_i\) 。问每个人最后糖果数量的期望值,模 \(10^9+7\)。
限制条件:\(1\le b_i \le n \le 5×10^5,1 \le a_i,w_i \le 10^9\)。
题解:就是求每个孩子能触发自己的事件的概率。如果 \(a_i<a_{b_i}\),无论如何都能触发;如果 \(a_i<a_{b_i}+w_{b_i}\) ,那就要求第 \(i\) 个事件发生在第 \(b_i\) 个事件之后,并且第 \(b_i\) 个事件成功触发,这个概率一会再考虑;如果 \(a_i \ge a_{b_i}+w_{b_i}\),那么无论如何都不能触发。
第二种情况是我们需要解决的,也就是第 \(i\) 个事件前面有多少个事件要按顺序发生了第 \(i\) 个事件才能够发生。我们考虑从 \(b_i\) 到 \(i\) 连一条边,那么 \(i\) 号点在图中的深度就对应它要在多少事件后面发生。而这样每个点最多只有一条入边,不会形成环,可以之间建图然后bfs求各个点的深度。但是要注意!最开始只有入度为0并且对应事件一定会发生的点能被加入到队列中。 如果这个事件已经不可能发生了,那它后面的事件也是不可能发生的了。
那么如果一个事件前面有 \(k\) 个事件要按顺序发生,那么这个事件发生的概率是多少呢?其实很好求!因为任意两个事件的顺序都是随机的,然后我们要求这 \(k+1\) 个事件按照我们指定的顺序发生,也就是这些事件的全排列中的唯一一种,和其他事件的顺序是没有关系的,所以概率就是 \(\frac{1}{(k+1)!}\),预处理一下逆元就可以直接求出 \(a_i+\frac{w_i}{(k+1)!}\) 了!时间复杂度上,bfs图的部分为 \(O(n)\),求逆元的部分为 \(O(n\log M)\),M为模数,求答案的部分为 \(O(n)\)。所以总时间复杂度为 \(O(n\log M)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int M=1000000007;
ll qpow(ll a,ll b)
{ll ans=1;a%=M;while(b){if(b&1)ans=ans*a%M;a=a*a%M;b>>=1;}return ans;
}
int main()
{ios::sync_with_stdio(0);cin.tie(0);ll tttt=1;cin>>tttt;while(tttt--){ll n;cin>>n;vector<ll>a(n+7),b(n+7),w(n+7);for(int i=1;i<=n;i++){cin>>a[i];}for(int i=1;i<=n;i++){cin>>b[i];}for(int i=1;i<=n;i++){cin>>w[i];}vector<ll>dep(n+7,-1);vector<vector<ll>>G(n+7);vector<ll>per(n+7,1);vector<bool>yw(n+7,0);for(int i=1;i<=n;i++){if(a[b[i]]<=a[i]&&a[b[i]]+w[b[i]]>a[i]){G[b[i]].push_back(i);yw[i]=1;}}queue<pair<ll,ll>>q;for(int i=1;i<=n;i++){if(!yw[i]&&a[i]<a[b[i]]){q.push({i,1});}}while(!q.empty()){auto [u,now]=q.front();dep[u]=now;q.pop();for(auto i:G[u]){q.push({i,now+1});}}// for(int i=1;i<=n;i++)// {// cout<<dep[i]<<" \n"[i==n];// }for(int i=1;i<=n;i++){per[i]=per[i-1]*qpow(i,M-2)%M;}vector<ll>ans(n+7);for(int i=1;i<=n;i++){if(a[b[i]]+w[b[i]]>a[i]&&dep[i]!=-1)ans[i]=(a[i]+per[dep[i]]*w[i]%M)%M;elseans[i]=a[i];cout<<ans[i]<<" \n"[i==n];}}return 0;
}
加油加油!