当前位置: 代码迷 >> 综合 >> Codeforces Round 1286 简要题解
  详细解决方案

Codeforces Round 1286 简要题解

热度:15   发布时间:2023-12-23 22:52:17.0

A. Garland

B. Numbers on Tree

C2. Madhouse (Hard version)

D. LCC

注意到如果存在相遇点对,第一次相遇的一定是相邻的点。
那么考虑枚举所有的相邻点对和它们的初始方向,可以得到O(n)\mathcal O(n)O(n)组可能的第一次相遇时间。给它们排序后,只要对每一组算出恰好是该时间相遇的概率即可,差分一下变为给定O(n)\mathcal O(n)O(n)个时刻tit_iti?,算出前tit_iti?时刻都没有相遇的概率。
对于一个固定的时刻ttt计算的话,相当于对每个相邻点对(i,i+1)(i,i+1)(i,i+1),给定它们之间可能的状态(iii的方向与i?1i-1i?1的方向间的关系),可以写成一个2?22\ast22?2的矩阵,那么从左到右做一个矩阵乘法即可求解。现在要对O(n)\mathcal O(n)O(n)个时刻求,可以排序后对时刻从大到小扫描线,每次加入若干个相邻点对间可能的状态,也即会修改若干个矩阵,那么拿线段树维护矩阵乘法即可。
时间复杂度为O(nlog?n)\mathcal O(n\log n)O(nlogn)

#include <bits/stdc++.h>
#define MOD 998244353
#define last last2using namespace std;typedef long long ll;ll pow_mod(ll x,int k) {
    ll ans=1;while (k) {
    if (k&1) ans=ans*x%MOD;x=x*x%MOD;k>>=1;}return ans;
}struct Matrix {
    int num[2][2],n,m;Matrix() {
    }Matrix(int a,int b):n(a),m(b) {
    memset(num,0,sizeof(num));}
};Matrix operator * (Matrix a,Matrix b) {
    Matrix c(a.n,b.m);for(int i=0;i<c.n;i++)for(int j=0;j<c.m;j++)for(int k=0;k<a.m;k++) c.num[i][j]=(c.num[i][j]+(ll)a.num[i][k]*b.num[k][j])%MOD;return c;
}Matrix fir[100005];namespace SGT {
    Matrix mulv[400000];void build(int l,int r,int o) {
    if (l==r) mulv[o]=fir[l];else {
    int m=((l+r)>>1);build(l,m,o*2);build(m+1,r,o*2+1);mulv[o]=mulv[o*2+1]*mulv[o*2];}
}void update(int l,int r,int o,int p) {
    if (l==r) mulv[o]=fir[l];else {
    int m=((l+r)>>1);if (m>=p) update(l,m,o*2,p);else update(m+1,r,o*2+1,p);mulv[o]=mulv[o*2+1]*mulv[o*2];}
}Matrix query() {
    return mulv[1];
}}const ll inv=pow_mod(100,MOD-2);ll num[100005],pos[100005],val[100005];
bool f[100005][4];void getMatrix(int x,int n) {
    fir[x]=Matrix(2,2);if (f[x][0]) fir[x].num[1][1]=(1LL-num[x]+MOD)%MOD;if (f[x][1]) fir[x].num[0][0]=num[x];if (f[x][2]) fir[x].num[1][0]=(1LL-num[x]+MOD)%MOD;if (f[x][3]) fir[x].num[0][1]=num[x];
}ll query() {
    Matrix t=SGT::query();Matrix v(2,1);v.num[0][0]=num[1];v.num[1][0]=(1LL-num[1]+MOD)%MOD;v=t*v;return (v.num[0][0]+v.num[1][0])%MOD;
}struct Frac {
    ll x,y;int kind,id;Frac() {
    }Frac(ll a,ll b,int c,int d):x(a),y(b),kind(c),id(d) {
    }bool operator < (const Frac & b) const {
    return x*b.y<y*b.x;}
};Frac a[400005];int main() {
    int n;scanf("%d",&n);for(int i=1;i<=n;i++) {
    scanf("%lld%lld%lld",&pos[i],&val[i],&num[i]);num[i]=num[i]*inv%MOD;}if (n==1) {
    puts("0");return 0;}int cnt=0;for(int i=2;i<=n;i++) {
    if (val[i-1]<val[i]) a[++cnt]=Frac(pos[i]-pos[i-1],val[i]-val[i-1],0,i);else f[i][0]=1;if (val[i]<val[i-1]) a[++cnt]=Frac(pos[i]-pos[i-1],val[i-1]-val[i],1,i);else f[i][1]=1;a[++cnt]=Frac(pos[i]-pos[i-1],val[i]+val[i-1],2,i);f[i][3]=1;getMatrix(i,n);}sort(a+1,a+cnt+1);SGT::build(2,n,1);ll ans=0,last=query();for(int i=cnt;i>0;i--) {
    f[a[i].id][a[i].kind]=1;getMatrix(a[i].id,n);SGT::update(2,n,1,a[i].id);ll v=query();ans=(ans+(v-last+MOD)*a[i].x%MOD*pow_mod(a[i].y,MOD-2))%MOD;last=v;}printf("%lld\n",ans);return 0;
}

E. Fedya the Potter Strikes Back

考虑每次加入cic_ici?wiw_iwi?,求出所有右端点为iii的区间的贡献。
注意到对于一个固定的左端点lll,满足它是suspiciousness的右端点rrr形成一个≥l\geq ll的前缀区间。那么我们可以维护所有当前suspiciousness的区间的左端点的集合,支持几种操作:插入一个元素,删除一个元素,对于集合中元素的权值取min?\minmin,查询集合中元素和。显然,这可以通过维护权值的单调栈,及用树状数组维护支持查询区间元素来完成。这样,我们的关键问题在于每次删去所有当前集合中,满足[l,i][l,i][l,i]suspiciousness的左端点lll
S[1,i]S[1,i]S[1,i]的最长border长度为failifail_ifaili?,这可以用KMP算法求出。那么每次集合中应保留的元素即为1,i?faili+1,i?failfaili+1,?1,i-fail_i+1,i-fail_{fail_i}+1,\cdots1,i?faili?+1,i?failfaili??+1,?。考虑求出所有上次存在,这次不存在的lll。根据KMP的过程,我们要不断跳i?1i-1i?1failfailfail链直至重新匹配上,那么跳过的lenlenlen对应的i?leni-leni?len就应该被删掉,且接下来要删去的长度即为failifail_ifaili?要删去的长度。可以发现删去的长度也形成了一个树形结构,那么每次直接暴力跳到根即可得到所有应当被删去的长度。
时间复杂度为O(nlog?n)\mathcal O(n\log n)O(nlogn)

#include <bits/stdc++.h>
#define lowbit(x) (x&-x)
#define FR first
#define SE secondusing namespace std;typedef long long ll;
typedef __int128 lll;
typedef pair<int,int> pr;namespace BIT {
    int sumv[600005];void add(int x,int n,int num) {
    for(;x<=n;x+=lowbit(x)) sumv[x]+=num;
}int sum(int x) {
    int s=0;for(;x;x-=lowbit(x)) s+=sumv[x];return s;
}int sum(int l,int r) {
    return sum(r)-sum(l-1);
}}char str[600005];int fail[600005],ed[600005]; 
int fa[600005],len[600005],cur[600005];
int num[600005],st[600005];void output(lll x) {
    if (!x) putchar('0');char str[30];int cnt=0;for(;x;x/=10) str[++cnt]=x%10;for(;cnt;cnt--) putchar(str[cnt]+'0');
}int main() {
    int n;scanf("%d",&n);int top=0,sz=0;ll s=0;lll ans=0;for(int i=1;i<=n;i++) {
    scanf("%s%d",str+i,&num[i]);str[i]='a'+(str[i]-'a'+ans)%26;num[i]^=(ans&((1<<30)-1));int cnt=0,x=fail[i-1];while (x&&str[x+1]!=str[i]) {
    cur[++cnt]=x;x=fail[x];}if (i>1&&str[x+1]==str[i]) x++;fail[i]=x;ed[i]=ed[x];for(int j=cnt;j>0;j--) {
    len[++sz]=cur[j];fa[sz]=ed[i];ed[i]=sz;}for(int j=ed[i];j;j=fa[j]) {
    int u=i-len[j],t=st[lower_bound(st+1,st+top+1,u)-st];BIT::add(u,n,-1);s-=num[t];}while (top&&num[st[top]]>=num[i]) {
    s-=(ll)(num[st[top]]-num[i])*BIT::sum(st[top-1]+1,st[top]);top--;}st[++top]=i;if (str[i]==str[1]) {
    BIT::add(i,n,1);s+=num[i];}ans+=s;output(ans);printf("\n");}return 0;
} 

F. Harry The Potter

考虑在两个进行了222操作间的下标(i,j)(i,j)(i,j)间连一条边,那么对于每个弱连通块SSS,至少需要进行∣S∣?1|S|-1S?1次操作才能全变为000。那么我们会选择尽量多的不交子集,使得每个子集SSS可以用∣S∣?1|S|-1S?1次操作全变为000,若最多能选出kkk个,答案即为n?kn-kn?k
考虑先预处理出每个集合SSS能否用∣S∣?1|S|-1S?1次操作全变为0。对于∣S∣=1|S|=1S=1的情况,显然当且仅当里面的元素为000。考虑∣S∣>1|S|>1S>1的情况(只考虑其中不包含000的,这显然不会变劣),注意到如果可行的话操作间形成了一棵树。对树按深度黑白染色的话可以得到两个非空子集S0S_0S0?S1S_1S1?,这里abs(∑T∈S0T?∑T∈S1T)<∣S∣abs(\sum_{T\in S_0}T-\sum_{T\in S_1}T)<|S|abs(TS0??T?TS1??T)<S,且要求∑T∈ST\sum_{T\in S}TTS?T∣S∣|S|S奇偶性相异。
事实上,对于一个集合SSS(不包含000),它能用∣S∣?1|S|-1S?1次操作全变为000的充要条件即为∑T∈ST\sum_{T\in S}TTS?T∣S∣|S|S奇偶性相异,且能够划分为两个非空子集S0′S_0'S0?S1′S_1'S1?,使得abs(∑T∈S0T?∑T∈S1T)<∣S∣abs(\sum_{T\in S_0}T-\sum_{T\in S_1}T)<|S|abs(TS0??T?TS1??T)<S。充分性可以构造得出:考虑先给S0′S_0'S0?S1′S_1'S1?分配222操作的额外的?1-1?1,使得分配完后两边总和相等(显然可以完成),不妨设∣S0′∣≤∣S1′∣|S_0'|\leq |S_1'|S0?S1?,若∣S0′∣=1|S_0'|=1S0?=1显然可行,否则取出一个x∈S0′x\in S_0'xS0?y∈S1′y\in S_1'yS1?,进行一次222操作,将yyy操作为000,至于?1-1?1的方向取决于哪边还剩余?1-1?1,操作完后S1′S_1'S1?中删去了一个元素,可以递归构造。
这样,我们可以对于每个SSS,枚举它的所有非空子集TTT判定,这样是O(3n)\mathcal O(3^n)O(3n)的,不太能通过。更优秀的做法是取一个块大小B≥?n2?B\geq \lceil \frac{n}{2}\rceilB?2n??,对于前BBB个数和n?Bn-Bn?B个数内部的所有不交集合对(p,q)(p,q)(p,q)p∩q=?p \cap q=\emptypq=?),按∑T∈pT?∑T∈qq\sum_{T\in p}T-\sum_{T\in q}qTp?T?Tq?q排序,代表pppqqq中的元素分到两侧。注意到这里的排序是对子集排序,可以类似倍增的思想,每次加入一个元素,维护当前的排序结果,这样按这个元素是否取及取的话分到哪一侧可以得到三个单调不降队列,对三个队列归并即可线性排序,复杂度为O(3B)\mathcal O(3^B)O(3B)
考虑对个数为n?Bn-Bn?B的一侧从小到大暴力枚举所有(p,q)(p,q)(p,q)的对,在BBB的一侧双指针,对每个p∪qp\cup qpq维护当前权值和不超过n?Bn-Bn?B一侧当前枚举的和的最大总和(这样可以尽可能接近),这样每扫一个元素直接枚举BBB一侧的所有可能的p∪qp\cup qpq(共2B2^B2B种)更新即可,还需要反过来再扫描一次处理BBB一侧和超过n?Bn-Bn?B一侧的情况,这样复杂度为O(2B?3n?B)\mathcal O(2^B\cdot 3^{n-B})O(2B?3n?B)。注意这里要求左边选的(p1,q1)(p_1,q_1)(p1?,q1?)与右边选的(p2,q2)(p_2,q_2)(p2?,q2?)满足p1∪q2≠?p_1\cup q_2\neq \emptyp1?q2???=?q1∪p2≠?q_1\cup p_2 \neq \emptyq1?p2???=?,我的实现是在上面做的过程中BBB的一侧只用p≠?p\neq \emptyp??=?q≠?q\neq \emptyq??=?的对(p,q)(p,q)(p,q)更新,对于至少一个为空的暴力枚举n?Bn-Bn?B一侧的算,这样复杂度不变。
这样对每个集合判定的总复杂度为O(3B+2B?3n?B)\mathcal O(3^B+2^B\cdot 3^{n-B})O(3B+2B?3n?B),简单分析一下可以知道当B=log?43?nB=\log_4{3}\cdot nB=log4?3?n时取到最优复杂度O((3log?43)n)\mathcal O((3^{\log_{4}{3}})^n)O((3log4?3)n),这比官方题解中的O((1+2)n)\mathcal O((1+\sqrt 2)^n)O((1+2 ?)n)更加优秀。
然后问题还剩下求出最多能选出多少不相交的,可以用∣S∣?1|S|-1S?1操作全部变为000的集合SSS。考虑一个集合幂级数AAA,其中[xS]A[x^S]A[xS]A当且仅当SSS非空且可以用∣S∣?1|S|-1S?1操作全部变为000时为111,其他时刻为000。那么能选出至少kkk个当且仅当Ak≠0A^k\neq 0Ak??=0(这里乘法定义为子集不交并卷积)。直接暴力枚举kkk的话是O(2n?n3)\mathcal O(2^n\cdot n^3)O(2n?n3)的,用倍增后二分的思想可以优化到O(2n?n2log?n)\mathcal O(2^n\cdot n^2\log n)O(2n?n2logn)。实际上虽然这部分渐进复杂度较低,但却是主要的时间瓶颈。
时间复杂度为O((3log?43)n+2n?n2log?n)\mathcal O((3^{\log_{4}{3}})^n+2^n\cdot n^2\log n)O((3log4?3)n+2n?n2logn)

#include <bits/stdc++.h>
#define FR first
#define SE second
#define inf 0x3f3f3f3f3f3f3f3fLLusing namespace std;typedef long long ll;
typedef unsigned int ui;
typedef pair<int,int> pr;ll num[25],sum[1<<20];
int bit[1<<20];
ui C[25][25];void pre(int n) {
    for(int i=1;i<(1<<n);i++) {
    bit[i]=bit[i>>1]+(i&1);for(int j=1;j<=n;j++)if ((i>>(j-1))&1) sum[i]+=num[j];}for(int i=0;i<=n;i++) C[i][0]=1;for(int i=1;i<=n;i++)for(int j=1;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j];
}inline ll getsum(pr x) {
    return sum[x.FR]-sum[x.SE];
}int getsub(int *p,pr *q,int n) {
    static pr q1[5000000],q2[5000000],q3[5000000];int sz=0;q[++sz]=pr(0,0);for(int i=1;i<=n;i++) {
    int x=p[i],v=(1<<(x-1));for(int j=1;j<=sz;j++) {
    q1[j]=q[j];q2[j]=pr(q[j].FR|v,q[j].SE);q3[j]=pr(q[j].FR,q[j].SE|v);}int r1=1,r2=1,r3=1,nsz=0;while (r1<=sz||r2<=sz||r3<=sz) {
    nsz++;if (r1<=sz&&(r2>sz||getsum(q1[r1])<getsum(q2[r2]))&&(r3>sz||getsum(q1[r1])<getsum(q3[r3])))q[nsz]=q1[r1++];else if (r2<=sz&&(r3>sz||getsum(q2[r2])<getsum(q3[r3])))q[nsz]=q2[r2++];else q[nsz]=q3[r3++];}sz=nsz;}return sz;
}pr a[100000],b[5000000];
bool f[1<<20];ll g[1<<20];void calc(int n,int m) {
    int cur[25];for(int i=1;i<=m;i++) cur[i]=i;int sz1=getsub(cur,a,m);int k=n-m;for(int i=1;i<=k;i++) cur[i]=m+i;int sz2=getsub(cur,b,k);for(int i=0;i<(1<<k);i++) g[i]=-inf;int rx=1;for(int i=1;i<=sz1;i++) {
    ll v=getsum(a[i]);int st=(a[i].FR^a[i].SE);if (a[i].FR&&a[i].SE&&abs(v)<bit[st]) f[st]=1;while (rx<=sz2&&getsum(b[rx])<=v) {
    if (b[rx].FR&&b[rx].SE) g[(b[rx].FR^b[rx].SE)>>m]=getsum(b[rx]);rx++;}for(int j=0;j<(1<<k);j++)if (v-g[j]<bit[st^(j<<m)]) f[st^(j<<m)]=1;}for(int i=0;i<(1<<k);i++) g[i]=inf;int lx=sz2;for(int i=sz1;i>0;i--) {
    ll v=getsum(a[i]);int st=(a[i].FR^a[i].SE);while (lx&&getsum(b[lx])>=v) {
    if (b[lx].FR&&b[lx].SE) g[(b[lx].FR^b[lx].SE)>>m]=getsum(b[lx]);lx--;}for(int j=0;j<(1<<k);j++)if (g[j]-v<bit[st^(j<<m)]) f[st^(j<<m)]=1;}for(int i=1;i<=sz2;i++) {
    ll v=getsum(b[i]);int st=(b[i].FR^b[i].SE);if (b[i].FR&&b[i].SE&&abs(v)<bit[st]) f[st]=1;if (b[i].FR&&!b[i].SE) {
    for(int j=1;j<=sz1;j++)if (a[j].FR&&abs(v-getsum(a[j]))<bit[st^a[j].FR^a[j].SE]) f[st^a[j].FR^a[j].SE]=1; }if (!b[i].FR&&b[i].SE) {
    for(int j=1;j<=sz1;j++)if (a[j].SE&&abs(v-getsum(a[j]))<bit[st^a[j].FR^a[j].SE])f[st^a[j].FR^a[j].SE]=1;}}for(int i=1;i<=n;i++)if (!num[i]) f[1<<(i-1)]=1;for(int i=0;i<(1<<n);i++)if (!((sum[i]^bit[i])&1)) f[i]=0;
}void fwt(ui *p,int len) {
    for(int i=1;i<len;i<<=1)for(int j=0;j<len;j++)if (j&i) p[j]+=p[j^i];
}ui p[5][21][1<<20],q[21][1<<20];int solve(int n) {
    for(int i=0;i<(1<<n);i++)if (f[i]) p[0][bit[i]][i]=1;for(int i=0;i<=n;i++) fwt(p[0][i],1<<n);for(int i=1;i<5;i++)for(int j=0;j<(1<<n);j++)for(int k=0;k<=n;k++)for(int l=0;l+k<=n;l++) p[i][k+l][j]+=p[i-1][k][j]*p[i-1][l][j];for(int i=0;i<(1<<n);i++)for(int j=0;j<=n;j++) q[j][i]=C[bit[i]][j];int ans=0;for(int i=4;i>=0;i--) {
    ui sum=0;for(int j=0;j<(1<<n);j++) {
    ui s=0;for(int k=0;k<=n;k++) s+=q[k][j]*p[i][n-k][j];sum+=((bit[j]&1)?-1:1)*s;}if (sum) {
    ans^=(1<<i);for(int j=0;j<(1<<n);j++)for(int k=n;k>=0;k--) {
    for(int l=1;l+k<=n;l++) q[k+l][j]+=q[k][j]*p[i][l][j];q[k][j]=0;}}}return n-ans;
}int main() {
    int n;scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%lld",&num[i]);pre(n);int m=min(n>>1,6);calc(n,m);printf("%d\n",solve(n));return 0;
}
/* 4 2 3 10 9 */
  相关解决方案