2019.03.14 ZJOI2019模拟赛 解题报告
得分: \(100+100+0=200\)(\(T1\)在最后\(2\)分钟写了出来,\(T2\)在最后\(10\)分钟写了出来,反而\(T3\)写了\(4\)个小时爆\(0\))
\(T1\):风王结界
一道数学题,看完题解是无比的简单。
比赛时最后两分钟把式子凑出来了(\(RP\)爆表)。。。
题解详见这篇博客:【CF660E】Different Subsets For All Tuples(组合数学),即这题模数为\(10^9+7\)的版本。
下面直接给出代码:
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000000
#define Qinv(x) Qpow(x,X-2)
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,m,X;
I int Qpow(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}//快速幂
int main()
{
freopen("invisible.in","r",stdin),freopen("invisible.out","w",stdout);
RI i,ans,p1,p2,b1,b2;
scanf("%d%d%d",&n,&m,&X),ans=p1=Qpow(m,n),p2=1,b1=Qinv(m),b2=(1LL*2*m-1)%X;//初始化
for(i=0;i^n;++i) Inc(ans,1LL*p1*p2%X),p1=1LL*p1*b1%X,p2=1LL*p2*b2%X;//O(n)计算答案
return printf("%d",ans),0;//输出答案
}
\(T2\):此世全部之恶
手写\(bitset\)暴力卡过。。。
显然,前两个限制随便判即可,关键在于第三个限制。
考虑当矩阵中只有\(0,1\)时,我们可以用\(n\)个\(bitset\)来存下每一行的值。
对于每一对\(i,j\),按位或一下第\(i\)行和第\(j\)行的\(bitset\)。
如果\(a_{i,j}=1\),且按位或得到的结果中存在某一位为\(0\)(即第\(i\)行和第\(j\)行上这一位皆为\(0\),也就是最大值为\(0\)),则输出\(NO\),否则输出\(YES\)。
而这个做法显然可以根据一个常见的套路进行推广。
我们先初始化所有\(bitset\)所有值为\(0\)。
然后倒序枚举元素值,每次把值等于这个元素的位置在\(bitset\)中将值改为\(1\)。
每次改完之后,就按前面提到过的方法\(Check\)一遍即可。
这个做法的时间复杂度是\(O(\frac{n^3}{32})\),用\(C++\)自带的\(STL\)肯定过不去,于是就要手写+小卡常。
然后就能过了(主要是这题正解虽是\(O(n^2)\)却常数很大,因此这种暴力做法也能卡过)。
代码如下:
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 2000
#define V 10000
#define uint unsigned int
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define Gmax(x,y) (x<(y)&&(x=(y)))
using namespace std;
int n,a[N+5][N+5];vector<pair<int,int> > v[V+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
char c,*A,*B,FI[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}F;
class Bitset//手写bitset
{
private:
static Con int SZ=64;uint a[SZ];
public:
I Bitset() {memset(a,0,sizeof(a));}I void Set(CI x) {a[x>>5]|=1ull<<(x&31);}
I bool operator | (Con Bitset& x) Con//求两个bitset按位或之后是否存在某一位为0
{
static uint Sz=n-1>>5,Mx=(1ull<<((n-1)&31)+1)-1;
for(RI i=0;i^Sz;++i) if(~(a[i]|x.a[i])) return true;//判断对于前面完整的数字,是否存在某一位为0
return (a[Sz]|x.a[Sz])^Mx;//判断最后一个不完整的数字是否存在某一位为0
}
}s[N+5];
int main()
{
freopen("grail.in","r",stdin),freopen("grail.out","w",stdout);
RI i,j,k,sz,Mx=0;for(F.read(n),i=1;i<=n;++i) for(j=1;j<=n;++j) F.read(a[i][j]),Gmax(Mx,a[i][j]),v[a[i][j]].pb(mp(i-1,j-1));//读入,按值存储下每一个位置
for(i=1;i<=n;++i) {for(j=1;j^i;++j) if(a[i][j]^a[j][i]) return puts("NO"),0;if(a[i][i]) return puts("NO"),0;}//对于题目中的前两种限制进行判断
for(i=Mx;~i;--i)//倒序枚举元素值
{
for(sz=v[i].size(),j=0;j^sz;++j) s[v[i][j].fir].Set(v[i][j].sec);//把值等于这个元素的位置在bitset中将值改为1
for(j=0;j^sz;++j) (s[v[i][j].fir]|s[v[i][j].sec])&&(puts("NO"),exit(0),0);//如果按位或得到的结果中存在某一位为0,则输出NO
}return puts("YES"),0;//输出YES
}
\(T3\):俄刻阿诺斯
我们可以设从根节点到\(x\)号节点的路径上最小的高度为\(Min_x\)。
则我们先将\(B_i\)排个序,然后统计出对于每个\(B_i\),有多少个\(Min_x\ge B_i\),记作\(g_i\)。
则不难发现,若对于每个\(B_i\),都满足\(g_i\ge n-i+1\),即\(g_i-n+i-1\ge0\),就存在一种放下所有棒子的方式。
然后我们就可以用线段树维护\(g_i-i\)的最小值,然后二分答案,枚举在哪个点加,并用线段树进行验证即可。
然而这种做法复杂度其实是有一定问题的。。。
理论上来讲时间复杂度是\(O(nlognlog10^8)\),而这貌似已经\(TLE\)了。
所以,把我这份卡常一个晚上、最慢的点仍要\(3\)秒多的代码贴一下:
#pragma GCC optimize(2)
#pragma G++ optimize(2)
#pragma GCC optimize(3)
#pragma G++ optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200000
#define INF 1e8
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
#define min(x,y) ((x)<(y)?(x):(y))
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
int n,m,ee,h[N+5],a[N+5],lnk[N+5];struct edge {int to,nxt;}e[N<<1];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
char c,*A,*B,FI[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}F;
class SegmentTreeSolver
{
private:
#define GP(x) (lower_bound(a+1,a+m+1,(x)+1)-a-1)
#define Give(x,y) (h[y]<=h[Mn[x]]?(Mn[y]=y,SMn[y]=Mn[x]):(Mn[y]=Mn[x],SMn[y]=h[y]<=h[SMn[x]]?y:SMn[x]))
#define Link(x,y) (nxt[x]=lk[y],lk[y]=x)
int p[N+5],p_[N+5],fa[N+5],Mn[N+5],SMn[N+5],lk[N+5],nxt[N+5];
class SegmentTree//线段树
{
private:
#define STO l,hl,rt<<1
#define ORZ hl+1,r,rt<<1|1
#define PU(x) (Mn[x]=min(Mn[x<<1],Mn[x<<1|1]))
#define PD(x) (F[x]&&(Mn[x<<1]+=F[x],F[x<<1]+=F[x],Mn[x<<1|1]+=F[x],F[x<<1|1]+=F[x],F[x]=0))
int n,Mn[N<<2],F[N<<2];
void bld(CI l,CI r,CI rt) {if(!(l^r)) return (void)(Mn[rt]=-n+l-1);int hl=l+r>>1;bld(STO),bld(ORZ),PU(rt);}//建树,初始化每个点的权值
public:
I void Init(CI t) {bld(1,n=t,1);}I bool Check() {return Mn[1]>=0;}
I void Update(CI p,CI v)//修改(为卡常将其写成了非递归)
{
if(!p) return;static int l,r,hl,rt;l=1,r=n,hl,rt=1;
W(r>p) PD(rt),hl=l+r>>1,p<=hl?(r=hl,rt<<=1):(Mn[rt<<1]+=v,F[rt<<1]+=v,l=hl+1,rt=rt<<1|1);
Mn[rt]+=v,F[rt]+=v;W(rt>>=1) PU(rt);
}
#undef STO
#undef ORZ
}T;
void dfs(CI x) {for(int i=lnk[x];i;i=e[i].nxt) e[i].to^fa[x]&&(fa[e[i].to]=x,Give(x,e[i].to),dfs(e[i].to),0);}//DFS预处理
I bool Check(CI x,CI v)//验证答案
{
static int i,res;for(i=lk[x];i;i=nxt[i]) T.Update(p[i],-1),T.Update(p_[i]=GP(min(h[x]+v,h[SMn[i]])),1);//修改
for(res=T.Check(),i=lk[x];i;i=nxt[i]) T.Update(p[i],1),T.Update(p_[i],-1);return res;//记录是否合法后,把前面改过的改回去
}
public:
I void Solve()
{
m>n&&(puts("-1"),exit(0),0),sort(a+1,a+m+1),h[0]=INF,dfs(Mn[1]=1),T.Init(m);
RI i,STO,hl,ORZ,f,ans=INF;for(i=1;i<=n;++i) T.Update(p[i]=GP(h[Mn[i]]),1),Link(i,Mn[i]);T.Check()&&(puts("0"),exit(0),0);//初始化求出g[i]
STO=1,ORZ=INF;W(STO<=ORZ) {for(f=0,i=1;i<=n&&!f;++i) Check(i,hl=STO+ORZ>>1)&&(f=1);f?(ans=hl,ORZ=hl-1):STO=hl+1;}//二分
ans==INF?puts("-1"):printf("%d",ans);//输出答案
}
}S;
int main()
{
freopen("oceanus.in","r",stdin),freopen("oceanus.out","w",stdout);
RI i,x,y;for(F.read(n),i=1;i<=n;++i) F.read(h[i]);for(i=1;i^n;++i) F.read(x,y),add(x,y),add(y,x);//读入数据
for(F.read(m),i=1;i<=m;++i) F.read(a[i]);return S.Solve(),0;//读入数据
}