【摸鱼on牛客】2020ICPC 小米 网络选拔赛第一场
2020ICPC 小米 网络选拔赛第一场
前言
前面三个多小时只有两个人在肝题,太艰难了QAQ
好在还是摸进决赛了,and充电宝++(* ̄︶ ̄)
训练情况
训练时出现的问题
前期的问题还是签到的速度,从10min+才开始有过题,跟第一梯队还是有一定差距。。
中期很顺利,几乎没有罚时。。
后期在开构造题G的时候没有想清楚就交了。还是抱有侥幸心理啊。。。然后罚时就炸了。后面是真的慌了,连个最基础的线段树都出了好多错。
以后写题还是得想清楚再交,暴力的算法最多交一发试试水。。
部分题解
G Tree Projection
链接
题意
给定一颗\(n\)个点的无根树的某个拓扑序A和DFS序B,要求求出一颗符合的树。\(1\leq n \leq 2 \times 10^5\) 。
题解
若\(A_1\)与\(B_1\)相等,那么直接让其余所有点连向\(A_1\),显然这是合理的。
然后考虑不相等的情况,这个时候我们先找到\(B_1\)在\(A\)中的位置,然后在这之前的\(A\)中的数显然在\(B_1\)为根的树种在同一颗子树中,我们可以在\(B\)中找到这颗子树占据的范围,然后根据这范围我们能将问题分成两个部分:
1.一部分有\(B_1\),这部分还包含不在那颗子树内的其他点,找到这些点在A中的位置后,得到新的子问题,这个问题中符合\(A_1=B_1\)。
2.这部分只包含之前分出来的子树,重复上面的操作递归即可。
按照上面的操作,我们发现这个问题是一定有解的。
我刚开想到这些后直接写,然后T飞了。。
如何在保证复杂度的情况下完成上面的操作?
只需要开一颗线段树维护一下前\(i\)个A中的点在B中所需的范围,在删B中的点时支持单点修改即可。
在B中删点时将指针从两端往中间扫,只扫到要删的点,这样每个点只扫到删掉一次,就能\(O(nlogn)\)了。
\(Code\)
#include<bits/stdc++.h>
#define LL long long
#define LD double
using namespace std;
const double PI=acos(-1);
const int INF=1e9;
const int N=2e5+10;
int n;
int cnt=0;
int u[N],v[N];
int w[N];
int a[N],b[N],t[N],byc[N],kcz[N];
struct node{
int l,r;
int mx,mn;
}d[N<<2];
#define ls i<<1
#define rs i<<1|1
void build(int l,int r,int i){
d[i].l=l;d[i].r=r;
if(l==r){
d[i].mn=byc[l];
d[i].mx=byc[l];
return;
}
int mid=l+r>>1;
build(l,mid,ls);
build(mid+1,r,rs);
d[i].mn=min(d[ls].mn,d[rs].mn);
d[i].mx=max(d[ls].mx,d[rs].mx);
}
void C(int i,int r){
if(d[i].l==d[i].r){
d[i].mx=-1;
d[i].mn=n+1;
return;
}
if(r<=d[ls].r) C(ls,r);
else C(rs,r);
d[i].mn=min(d[ls].mn,d[rs].mn);
d[i].mx=max(d[ls].mx,d[rs].mx);
}
int MN,MX;
void ask(int i,int r){
if(d[i].l==d[i].r){
MN=min(d[i].mn,MN);
MX=max(d[i].mx,MX);
return;
}
if(r<=d[ls].r) ask(ls,r);
else {
MN=min(MN,d[ls].mn);
MX=max(MX,d[ls].mx);
ask(rs,r);
}
return;
}
void get(int l,int r){
if(l>=r) return;
if(l+1==r){
++cnt;
u[cnt]=b[l];
v[cnt]=b[r];
return;
}
if(a[1]==b[l]){
for(int i=l+1;i<=r;++i){
++cnt;u[cnt]=b[l];v[cnt]=b[i];
}
return;
}
int x=kcz[b[l]];
MN=r+1;MX=l-1;
ask(1,x-1);
for(int i=l;i<MN;++i){
if(i>l){
++cnt;u[cnt]=b[l];v[cnt]=b[i];
}
C(1,kcz[b[i]]);
}
for(int i=MX+1;i<=r;++i){
if(i>l){
++cnt;u[cnt]=b[l];v[cnt]=b[i];
}
C(1,kcz[b[i]]);
}
++cnt;u[cnt]=b[l];v[cnt]=b[MN];
get(MN,MX);
}
int main(){
scanf("%d",&n);
int x;
for(int i=1;i<=n;++i) {
scanf("%d",&a[i]);
}
for(int i=1;i<=n;++i){
scanf("%d",&b[i]);kcz[b[i]]=i;
}
for(int i=1;i<=n;++i){
byc[i]=kcz[a[i]];
}
build(1,n,1);
for(int i=1;i<=n;++i){
kcz[a[i]]=i;
}
get(1,n);
puts("YES");
for(int i=1;i<=cnt;++i){
printf("%d %d\n",u[i],v[i]);
}
return 0;
}
K Sqrt Approaching
链接
题意
给出正整数A,B,n,要求C,D满足\(A/B<C/D<n^{0.5}\)。\(1\leq A,B \leq 10^{99990}\),\(2\leq n \leq 10^{9}\) 。
题解
原题面恶心的很。。但式子稍微推一下就变成上面那个题意啦。。
然后我就不会做了QAQ。不管怎么搞总是有很大误差的说。
然后题解神奇的给了个通解但没有给出证明QAQ(而且我也不会证,那这东西就当是公式记住吧)
C=(n+1)A+2nB; D=2A+(n+1)B
\(Code\)
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL P=998244353;
const int N=2e5+100;
const double PI=acos(-1);
char s[N];
LL a[N],b[N],c[N],d[N];
int L=100000,len;
LL n;
int main(){
int f;
for(int i=0;i<=100000;++i) a[i]=b[i]=c[i]=d[i]=0;
scanf("%s",s+1);len=strlen(s+1);
int ca=-1;
for(int i=len;i>=1;--i)a[++ca]=s[i]-'0';
scanf("%s",s+1);len=strlen(s+1);
int cb=-1;
for(int i=len;i>=1;--i)b[++cb]=s[i]-'0';
scanf("%lld",&n);
for(int i=0;i<=100000;++i){
c[i]=(n+1)*a[i]+(LL)2*n*b[i];
d[i]=(LL)2*a[i]+(n+1)*b[i];
}
for(int i=0;i<=100000;++i){
if(c[i]>=(LL)10){
c[i+1]+=c[i]/10;
c[i]=c[i]%(LL)10;
}
if(d[i]>=(LL)10){
d[i+1]+=d[i]/10;
d[i]=d[i]%(LL)10;
}
}
f=0;
for(int i=100000;i>=0;--i){
if(c[i]>0) f=1;
if(f) printf("%d",c[i]);
}
puts("");
f=0;
for(int i=100000;i>=0;--i){
if(d[i]>0) f=1;
if(f) printf("%d",d[i]);
}
puts("");
return 0;
}
H Grouping
链接
题意
将2n个数分成n对,每对的价值是两数的差的绝对值,一种分法的价值是每对的价值的方差,问所有分法的期望价值,对998244353取模。\(1\leq n \leq 10^{5}\) 。
题解
这题式子其实挺好推的,但是我队开题并不积极,基本都是跟榜。
然后这题基本没几个队过,就没开这题。
现在看来并不难啊。。血亏
过程直接放题解的截图吧。。
\(Code\)
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL P=998244353;
const int N=2e5+100;
const double PI=acos(-1);
LL ans;
LL n;
LL a[N],s[N],s2[N],g[N],f[N];
LL T[N];
LL qpow(LL x,LL y){
LL re=1;
while(y){
if(y&1) re=re*x%P;
x=x*x%P;y>>=1;
}
return re;
}
int main(){
LL res;
scanf("%lld",&n);
if(n==1){
puts("0");
return 0;
}
for(int i=1;i<=n+n;++i){
scanf("%lld",&a[i]);
}
sort(a+1,a+1+n+n);
for(LL i=1;i<=n+n;++i){
s[i]=(s[i-1]+a[i])%P;
s2[i]=(s2[i-1]+a[i]*a[i])%P;
g[i]=(i-1)*a[i]%P*a[i]%P;
g[i]=(g[i]+s2[i-1])%P;
g[i]=(g[i]-(LL)2*a[i]*s[i-1])%P;
g[i]+=g[i-1];
g[i]=(g[i]%P+P)%P;
f[i]=(i-1)*a[i]%P-s[i-1];
f[i]+=f[i-1];
f[i]=(f[i]%P+P)%P;
}
ans=g[n+n];
res=n*(n+n-1)%P;
ans=ans*qpow(res,P-2)%P;
T[0]=T[1]=1;
for(LL i=2;i<=n;++i) T[i]=T[i-1]*(i+i-1)%P;
LL val=g[n+n]*T[n-1]%P;
LL u=f[n+n]*f[n+n]%P,v;
for(LL i=1;i<=n+n;++i){
v=0;
v+=(i-1)*a[i]%P-s[i-1];
v+=(s[n+n]-s[i])-(n+n-i)*a[i]%P;
v=(v%P+P)%P;
v=v*v%P;
u-=v;
}
u=((u+g[n+n])%P+P)%P;
u=u*T[n-2]%P;
val=(val+u)%P*qpow(T[n]*n%P*n%P,P-2)%P;
ans=(ans-val+P)%P;
cout<<ans<<endl;
return 0;
}
/*
2
2 2 3 4
748683265
*/