2021.10.13 模拟赛题解
\(320\to 145\),我怕不是 sb,我紫菜/dk
T1
首先如果 \(\gcd(a,b)>1\) 答案就是 \(-1\)。
否则 \(a\perp b\),不妨设 \(a<b\),那么根据同余最短路的思想,对于所有 \(\bmod a\) 的剩余类,最小的能够表示出来的数必定是 \(b\) 的倍数。
以 \((a-1)b,(a-2)b,\cdots,2b,b\) 的顺序贪心取数即可。
T2
其实是一道不算太难的题,而由于 wssb 赛时只会 20/dk
下面默认 \(a_i\) 为小 B 第 \(i\) 次出的数,\(b_i\) 为小 A 拥有的数,与题目中人名相反(
首先考虑找到这个最大匹配数的等价表达,我们肯定会贪心地对每个数找到其能匹配的最小的值对吧,而此题值域不算大,这就启发我们把操作放到权值线段树上。我们以值域为下标建一棵线段树,对于每个区间,我们记录对这个区间贪心匹配后还剩多少个 \(a\) 中的数和多少个 \(b\) 中的数,以及总共进行了多少次匹配,然后求解答案就在剩余的数中二分找到最大的填上之后不影响答案的数即可,如果直接二分复杂度是 2log 的,由于此题数据范围只有 \(10^5\),可以通过,当然也可以线段树上二分实现 \(n\log n\) 的复杂度,不过由于本人太懒所以没有实现这个复杂度的做法(
T3
首先这个要么包含要么不交的条件显然暗示了一个树形结构,因此我们不管三七二十一先把树建出来。于是问题转化为,对于每个 \(k\in[1,m]\),我们要选出若干个点出来使得选出的点中,不存在 \(k\) 个点使得前一个点是后一个点的祖先,求选出的点的最大权值之和。
首先我们可以想到一个 DP:\(dp_{u,j}\) 表示在 \(u\) 中子树中选出若干个点,满足最大祖先嵌套关系为 \(j\) 层,选出的点的权值之和的最大值,转移就跑遍树形背包即可。这样复杂度是 \(n^2\) 的,不过注意到这个 DP 状态与深度有关,因此考虑长链剖分优化,每次两条链合并时,短链往长链合并复杂度是 \(\mathcal O(\text{短链长度})\) 的,不会出现问题,麻烦的是加入根节点的复杂度,因为加入根节点时,我们相当于往背包里加入一个元素,\(dp_{x,j}\leftarrow\max(dp_{x,j},dp_{x,j-1}+a_x)\),这样遍历还是要 \(\mathcal O(mxdep_x)\),无法通过。赛时我就是这一步卡了很久,心态爆炸。注意到 \(dp_{x,j}\) 是上凸的,因此其差分数组单调不增,也就是说我们这个 \(\max\) 肯定作用于一段后缀,稍微手推几组数据即可发现,该操作相当于往差分数组中加入一个元素 \(a_x\),用个 set
维护即可。
时间复杂度 \(n\log n\)。
const int MAXN=1e6;
int n,m,hd[MAXN+5],to[MAXN+5],nxt[MAXN+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
struct itvl{
int l,r,v,id;
itvl(int _l=0,int _r=0,int _v=0,int _id=0):
l(_l),r(_r),v(_v),id(_id){}
bool operator <(const itvl &rhs) const{
if(l^rhs.l) return l<rhs.l;
if(r^rhs.r) return r>rhs.r;
return id<rhs.id;
}
} a[MAXN+5];
int fa[MAXN+5];
int mxdep[MAXN+5],dson[MAXN+5];
void dfs0(int x){
mxdep[x]=0;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];dfs0(y);
if(mxdep[y]+1>mxdep[x]) mxdep[x]=mxdep[y]+1,dson[x]=y;
}
}
multiset<ll> stv;
void dfs(int x){
vector<vector<ll> > sdp;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==dson[x]) continue;
dfs(y);vector<ll> tmp;tmp.pb(0);int sz=stv.size();
for(int i=1;i<=sz;i++){
tmp.pb(*stv.rbegin());
stv.erase(stv.find(*stv.rbegin()));
} sdp.pb(tmp);
}
if(dson[x]) dfs(dson[x]);
for(int i=0;i<sdp.size();i++){
vector<ll> tmp;tmp.pb(0);
for(int j=1;j<sdp[i].size();j++){
tmp.pb(*stv.rbegin());
stv.erase(stv.find(*stv.rbegin()));
}
for(int j=1;j<sdp[i].size();j++){
ll v=tmp[j]+sdp[i][j];
stv.insert(v);
}
}
int val=a[x].v;stv.insert(val);
}
vector<pair<pii,int> > add[MAXN+5],del[MAXN+5];
int main(){
freopen("cover.in","r",stdin);
freopen("cover.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].v);
add[a[i].l].push_back(mp(mp(a[i].l,-a[i].r),i));
del[a[i].r+(a[i].l==a[i].r)].pb(mp(mp(a[i].l,-a[i].r),i));
}
for(int i=1;i<=n;i++) sort(add[i].begin(),add[i].end());
multiset<pair<pair<int,int>,int> > st;
for(int i=1;i<=n;i++){
for(auto p:del[i]) st.erase(p);
for(auto p:add[i]){
if(!st.empty()) fa[p.se]=st.rbegin()->se;
st.insert(p);
}
}
for(int i=1;i<=m;i++){
if(!fa[i]) adde(m+1,i);
else adde(fa[i],i);
}
dfs0(m+1);dfs(m+1);ll curs=0;
for(int i=1;i<=m;i++){
if(!stv.empty()) curs+=(*stv.rbegin()),stv.erase(stv.find(*stv.rbegin()));
printf("%lld%c",curs," \n"[i==m]);
}
return 0;
}
T4
为啥感觉 T4 是这场比赛最签的题啊 qwq
首先考虑计算后手赢的概率,拿 \(1\) 减一下即可得到答案。
对于一个生成树,其对答案的贡献显然是 \(\dfrac{1}{C}[x^0]\prod\limits_{e\in T}\prod\limits_{v\in S_e}(\dfrac{3}{4}x^0+\dfrac{1}{4}x^v)\),其中 \(C\) 为生成树棵数,幂级数的乘积定义为它们的 XOR 卷积。按照 P5406 找树 的套路,我们可以考虑对每一条边上的幂级数进行一遍 FWTxor,这样对每一位跑一遍矩阵树定理再 IFWTxor 回去即可得到答案。
于是现在问题转化为如何求每一条边上的集合幂级数之积,直接求是 \(|S_e|·2^m·n^2\) 的,无法通过,不过注意到对所有形如 \((\dfrac{3}{4}x^0+\dfrac{1}{4}x^v)\) 的幂级数 FWTxor 后,每一位的系数只可能是 \(\dfrac{1}{2}\) 或 \(1\),并且对于一位 \(x^t\),其系数为 \(\dfrac{1}{2}\) 的次数就是 \(\dfrac{|S_e|-\sum\limits_{v\in S_e}(-1)^{|v\&t|}}{2}\),可以用鸡兔同笼解释。注意到后面那个 \(\sum\) 相当于对 \(\sum\limits_{v\in S_e}x^v\) 进行 FWTxor 后,\(x^t\) 前的系数,这个一遍 FWTxor 即可求出来,这样复杂度就降到了 \(n^3·2^m+n^2·2^m·m\)
using namespace fastio;
const int MAXN=70;
const int MAXP=1024;
const int INV2=499122177;
const int INV4=(MOD+INV2)>>1;
void add(int &x,int v){((x+=v)>=MOD)&&(x-=MOD);}
void sub(int &x,int v){((x-=v)<0)&&(x+=MOD);}
int qpow(int x,int e){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
int n,m,cnt[MAXP+5],pwiv[MAXP+5];
int a[MAXP+5][MAXN+5][MAXN+5],res[MAXP+5],b[MAXN+5][MAXN+5];
void addb(int u,int v,int w){
sub(b[u-1][v-1],w);sub(b[v-1][u-1],w);
add(b[u-1][u-1],w);add(b[v-1][v-1],w);
}
void adda(int x,int u,int v,int w){
// printf("%d %d %d %d\n",x,u,v,w);
sub(a[x][u-1][v-1],w);sub(a[x][v-1][u-1],w);
add(a[x][u-1][u-1],w);add(a[x][v-1][v-1],w);
}
void FWTxor(int *a,int len,int type){
for(int i=2;i<=len;i<<=1)
for(int j=0;j<len;j+=i)
for(int k=0;k<(i>>1);k++){
int X=a[j+k],Y=a[(i>>1)+j+k];
a[j+k]=1ll*type*(X+Y)%MOD;
a[(i>>1)+j+k]=1ll*type*(X-Y+MOD)%MOD;
}
}
int getdet(int x){
int sgn=1;
for(int i=1;i<n;i++){
int t=i;
for(int j=i+1;j<n;j++) if(a[x][j][i]) t=j;
if(t^i){
sgn=MOD-sgn;
for(int j=i;j<n;j++) swap(a[x][t][j],a[x][i][j]);
} int iv=qpow(a[x][i][i],MOD-2);
for(int j=i+1;j<n;j++){
for(int k=i+1;k<n;k++) a[x][j][k]=(a[x][j][k]+1ll*a[x][i][k]*(MOD-a[x][j][i])%MOD*iv)%MOD;
a[x][j][i]=0;
}
} int res=sgn;
for(int i=1;i<n;i++) res=1ll*res*a[x][i][i]%MOD;
return res;
}
int getdet0(){
int sgn=1;
for(int i=1;i<n;i++){
int t=i;
for(int j=i+1;j<n;j++) if(b[j][i]) t=j;
if(t^i){
sgn=MOD-sgn;
for(int j=i;j<n;j++) swap(b[t][j],b[i][j]);
} int iv=qpow(b[i][i],MOD-2);
for(int j=i+1;j<n;j++){
for(int k=i+1;k<n;k++) b[j][k]=(b[j][k]+1ll*b[i][k]*(MOD-b[j][i])%MOD*iv)%MOD;
b[j][i]=0;
}
} int res=sgn;
for(int i=1;i<n;i++) res=1ll*res*b[i][i]%MOD;
return res;
}
int main(){
freopen("gemo.in","r",stdin);
freopen("gemo.out","w",stdout);
read(n);read(m);
for(int i=(pwiv[0]=1);i<=MAXP;i++) pwiv[i]=1ll*pwiv[i-1]*INV2%MOD;
for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++){
int len;read(len);
if(len){
addb(i,j,1);memset(cnt,0,sizeof(cnt));
for(int _=1;_<=len;_++){int x;scanf("%d",&x);cnt[x]++;}
FWTxor(cnt,1<<m,1);
for(int k=0;k<(1<<m);k++) if(cnt[k]>1e6) cnt[k]-=MOD;
// for(int k=0;k<(1<<m);k++) printf("%d%c",cnt[k]," \n"[k==((1<<m)-1)]);
for(int k=0;k<(1<<m);k++) adda(k,i,j,pwiv[(len-cnt[k])/2]);
}
}
for(int i=0;i<(1<<m);i++) res[i]=getdet(i);
FWTxor(res,1<<m,INV2);int val=getdet0();
printf("%d\n",(1-1ll*res[0]*qpow(val,MOD-2)%MOD+MOD)%MOD);
return 0;
}