其他分享
首页 > 其他分享> > 进击的刷题人 第5话 初阵

进击的刷题人 第5话 初阵

作者:互联网

文章目录

ACL1C Moving Pieces

本质上, o o o阻挡不了 o o o的前进,假设 o 1 o_1 o1​只考虑 # \# #能到达位置 p p p,但是被 o 2 o_2 o2​挡住了,可以让 o 2 o_2 o2​跑到位置 p p p, o 1 o_1 o1​跑到 o 2 o_2 o2​,步数不变,相当于 o o o可以穿透 o o o,但最终不能重叠。
考虑费用流模型,建立两个点 s , t s,t s,t作为源点和汇点, s s s连接 o o o流量 1 1 1费用 0 0 0, o o o连接可以到达的位置 p p p流量 1 1 1费用为步数(注意到 a [ i ] [ j ] = ′ o ′ a[i][j]='o' a[i][j]=′o′的点需要拆成两个点),位置 p p p连接 t t t流量 1 1 1费用 0 0 0,但我们需要的是最大费用,从而把费用取反,为了避免出现负环,把 o o o连 p p p的边的权值加上一个正值 l o w low low使得费用大于 0 0 0。最后,设最大流量为 a n s ans ans,最小费用为 r e s res res,则答案 − ( r e s − l o w ∗ a n s ) -(res-low*ans) −(res−low∗ans),这是因为,每单位流量的路线都是按照 s → ′ o ′ → p → t s\rightarrow 'o'\rightarrow p\rightarrow t s→′o′→p→t这样走的,而仅有 ′ o ′ → p 'o'\rightarrow p ′o′→p加了 l o w low low,所以流量是多少, l o w low low就被多计算了多少次,且流量是 ′ o ′ 'o' ′o′的数量为一个定值。
此题图比较稀疏,但流量最多是 n 2 n^2 n2,所以考虑用 s p f a spfa spfa费用流, z k w zkw zkw费用流会 T L E TLE TLE。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=5555,low=2500;
int n,m,s,t,x,y,ans,res;
int flow[N][N],wi[N][N],tot,head[N],nex[N*N*2],to[N*N*2];
void add(int u,int v,int f,int w)
{
    to[++tot]=v;nex[tot]=head[u];head[u]=tot;
    to[++tot]=u;nex[tot]=head[v];head[v]=tot;
    flow[u][v]=f;wi[u][v]=w;
    flow[v][u]=0;wi[v][u]=-w;
}
bool vis[N];int d[N],ne[N];
bool spfa()
{
    memset(vis,false,sizeof(vis));
    memset(d,inf,sizeof(d));
    deque<int>q;q.push_back(t);
    vis[t]=true;d[t]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop_front();
        if(u==s) return true;
        vis[u]=false;
        for(int i=head[u];i;i=nex[i])
        {
            int v=to[i];
            if(flow[v][u]&&d[v]>d[u]+wi[v][u])
            {
                ne[v]=u;
                d[v]=d[u]+wi[v][u];
                if(!vis[v])
                {
                    vis[v]=true;
                    if(!q.empty()&&d[v]<d[q.front()])
                        q.push_front(v);
                    else q.push_back(v);
                }
            }
        }
    }
    return d[s]!=inf;
}
void zkw()
{
    while(spfa())
    {
        int mn=1e9;
        for(int i=s;i!=t;i=ne[i])
            mn=min(mn,flow[i][ne[i]]);
        ans+=mn;
        for(int i=s;i!=t;i=ne[i])
            res+=mn*wi[i][ne[i]],flow[i][ne[i]]-=mn,flow[ne[i]][i]+=mn;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    char a[55][55];
    for(int i=1;i<=n;i++)
        scanf("%s",a[i]+1);
    int f[55][55],g[55][55];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
    {
        if(a[i][j]!='#') f[i][j]=++y;
        if(a[i][j]=='o') g[i][j]=++x;
    }
    s=x+y+1;t=x+y+2;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
    {
        if(a[i][j]=='o')
        {
            add(s,g[i][j],1,0);
            int c[55][55];
            memset(c,0,sizeof(c));
            c[i][j]=1;
            for(int i2=i;i2<=n;i2++)
                for(int j2=j;j2<=m;j2++)
                if(a[i2][j2]!='#')
                c[i2][j2]|=c[i2-1][j2]|c[i2][j2-1];
            for(int i2=i;i2<=n;i2++)
                for(int j2=j;j2<=m;j2++)
                if(c[i2][j2])
                    add(g[i][j],f[i2][j2]+x,1,-(i2-i+j2-j)+low);
        }
        if(a[i][j]!='#')
            add(f[i][j]+x,t,1,0);
    }
    zkw();
    printf("%d\n",low*ans-res);
}

ACL1D Keep Distances

令 a 1 = l a_1=l a1​=l, a i + 1 = m i n ( j ∣ l ≤ j ≤ r , X a i + 1 − X a i ≥ k ) a_{i+1}=min(j|l\le j\le r,X_{a_{i+1}}-X_{a_i}\ge k) ai+1​=min(j∣l≤j≤r,Xai+1​​−Xai​​≥k),可以得到一个序列 a 1 , a 2 , . . . , a m a_1,a_2,...,a_m a1​,a2​,...,am​。
令 b m = r b_m=r bm​=r, b i − 1 = m a x ( j ∣ l ≤ j ≤ r , X b i − X b i − 1 ≥ k ) b_{i-1}=max(j|l\le j\le r,X_{b_{i}}-X_{b_{i-1}}\ge k) bi−1​=max(j∣l≤j≤r,Xbi​​−Xbi−1​​≥k),可以得到一个序列 b 1 , b 2 , . . . , b m b_1,b_2,...,b_m b1​,b2​,...,bm​。
可以证明 b i ≥ a i b_i\ge a_i bi​≥ai​, b i + 1 > a i b_{i+1}>a_i bi+1​>ai​,且 [ a i , b i ] [a_i,b_i] [ai​,bi​]直接的数都可以取。
答案为 ∑ i = 1 m ( b i − a i + 1 ) \sum\limits_{i=1}^m(b_i-a_i+1) i=1∑m​(bi​−ai​+1)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,k,q,a[N],dep[N],fa1[N][20],fa2[N][20];
ll sum1[N],sum2[N];
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    sum1[n]=n;
    for(int i=n-1;i>=1;i--)
    {
        int pos=lower_bound(a+1,a+1+n,a[i]+k)-a;
        if(pos>n)
        {
            sum1[i]=i;continue;
        }
        dep[i]=dep[pos]+1;
        fa1[i][0]=pos;
        for(int j=1;j<=19;j++) fa1[i][j]=fa1[fa1[i][j-1]][j-1];
        sum1[i]=sum1[pos]+i;
    }
    sum2[1]=1;
    for(int i=2;i<=n;i++)
    {
        int pos=upper_bound(a+1,a+1+n,a[i]-k)-a-1;
        if(pos<1)
        {
            sum2[i]=i;continue;
        }
        fa2[i][0]=pos;
        for(int j=1;j<=19;j++) fa2[i][j]=fa2[fa2[i][j-1]][j-1];
        sum2[i]=sum2[pos]+i;
    }
    scanf("%d",&q);
    while(q--)
    {
        int l,r;scanf("%d%d",&l,&r);
        int x=l,y=r;
        for(int i=19;i>=0;i--)
        {
            if(fa1[x][i]&&fa1[x][i]<=r)
                x=fa1[x][i];
            if(fa2[y][i]&&fa2[y][i]>=l)
                y=fa2[y][i];
        }
        ll ans=sum2[r]-sum2[fa2[y][0]]-(sum1[l]-sum1[fa1[x][0]])+dep[l]-dep[x]+1;
        printf("%lld\n",ans);
    }
}

ACL1E Shuffle Window

首先,重述问题。一开是集合中有 k k k个数 1 , 2 , . . . , k 1,2,...,k 1,2,...,k和一个空数组 B B B,对 i = k + 1 , k + 2 , . . . , n i=k+1,k+2,...,n i=k+1,k+2,...,n依次执行以下操作:
从集合中随机选择一个数放入数组 B B B的末尾,并将这个数从集合中删除,然后,将 i i i放入集合中。
最后,将集合中 k k k张牌以随机的顺序放入数组 B B B的末尾。
由于逆序对只于相对顺序有关,从而,对于一对位置 ( i , j ) ( i < j ) (i,j)(i<j) (i,j)(i<j),只需关注 i , j i,j i,j交换位置的概率。
两个数相对位置能发生改变,当且仅当某一个时刻它们能同时处于集合中,设 t i t_i ti​为 i i i加入集合的时间,每一个时刻集合中的数都有 k − 1 k \frac{k-1}{k} kk−1​的概率不变扔掉,则 i , j i,j i,j能同时处于一个集合的概率为 ( k − 1 k ) t j − t i (\frac{k-1}{k})^{t_j-t_i} (kk−1​)tj​−ti​。
当 i , j i,j i,j处于一个集合,则它们产生逆序对的概率为 1 2 \frac{1}{2} 21​。则答案为:
∑ j = 1 n ∑ i = 1 j − 1 [ p i < p j ] ( k − 1 k ) t j − t i 2 + [ p i > p j ] ( 1 − ( k − 1 k ) t j − t i ) = ( ∑ j = 1 n ( k − 1 k ) t j ∑ i = 1 j − 1 [ p i < p j ] ( k − 1 k ) − t i 2 ) + { ∑ j = 1 n ( ∑ i = 1 j − 1 [ p i > p j ] ) − ( k − 1 k ) t j ∑ i = 1 j − 1 [ p i > p j ] ( k − 1 k ) − t i } \sum\limits_{j=1}^n\sum\limits_{i=1}^{j-1}\frac{[p_i<p_j](\frac{k-1}{k})^{t_j-t_i}}{2}+[p_i>p_j](1-(\frac{k-1}{k})^{t_j-t_i})\\ =(\sum\limits_{j=1}^n\frac{(\frac{k-1}{k})^{t_j}\sum\limits_{i=1}^{j-1}[p_i<p_j](\frac{k-1}{k})^{-t_i}}{2})+\\\{\sum\limits_{j=1}^n(\sum\limits_{i=1}^{j-1}[p_i>p_j])-(\frac{k-1}{k})^{t_j}\sum\limits_{i=1}^{j-1}[p_i>p_j](\frac{k-1}{k})^{-t_i}\} j=1∑n​i=1∑j−1​2[pi​<pj​](kk−1​)tj​−ti​​+[pi​>pj​](1−(kk−1​)tj​−ti​)=(j=1∑n​2(kk−1​)tj​i=1∑j−1​[pi​<pj​](kk−1​)−ti​​)+{j=1∑n​(i=1∑j−1​[pi​>pj​])−(kk−1​)tj​i=1∑j−1​[pi​>pj​](kk−1​)−ti​}
k − 1 k \frac{k-1}{k} kk−1​为常数,对于 p i < p j p_i<p_j pi​<pj​这样的条件,可以采用树状数组优化,从而可以在 O ( n l o g n ) O(nlogn) O(nlogn)时间内得出答案。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5,mod=998244353;
int n,k;
ll f[N],invf[N],p[N],bit1[N],bit2[N];
void query(ll &ans1,ll &ans2,int x)
{
    while(x)
    {
        ans1=(ans1+bit1[x])%mod;
        ans2=(ans2+bit2[x])%mod;
        x-=x&-x;
    }
}
void add(int x,int v)
{
    while(x<=n)
    {
        bit1[x]=(bit1[x]+1)%mod;
        bit2[x]=(bit2[x]+v)%mod;
        x+=x&-x;
    }
}
ll qpow(ll a,ll n)
{
    ll ans=1;
    for(;n;n>>=1,a=a*a%mod)
        if(n&1) ans=ans*a%mod;
    return ans;
}
int main()
{
    cin>>n>>k;
    f[0]=invf[0]=1;
    f[1]=1ll*(k-1)*qpow(k,mod-2)%mod;
    invf[1]=qpow(f[1],mod-2);
    for(int i=2;i<N;i++)
        f[i]=f[i-1]*f[1]%mod,invf[i]=invf[i-1]*invf[1]%mod;
    for(int i=1;i<=n;i++) cin>>p[i];
    ll sum=0,ans=0;
    for(int i=1;i<=n;i++)
    {
        int t=i<=k?1:i-k+1;
        ll sum1=0,sum2=0;
        query(sum1,sum2,p[i]);
        ans=(ans+f[t]*sum%mod*(mod-mod/2))%mod;
        ans=(ans+i-1-sum1-f[t]*(sum-sum2))%mod;
        add(p[i],invf[t]);
        sum=(sum+invf[t])%mod;
    }
    ans=(ans+mod)%mod;
    printf("%lld\n",ans);
}

CF504E Misha and LCP on Tree

题:给出一棵 n n n结点的树,每个结点上有一个字符, ( x , y ) (x,y) (x,y)表示从点 x x x到点 y y y路径上字符组成的字符串。有 q q q个查询,每个查询给出 a , b , c , d a,b,c,d a,b,c,d,求 ( a , b ) (a,b) (a,b)和 ( c , d ) (c,d) (c,d)的最长公共前缀。( n , q ≤ 300000 n,q\le300000 n,q≤300000)。
解:定义串 s s s的哈希函数为 h s ( s ) = ∑ i = 1 n ( s i − ′ 0 ′ + 1 ) ∗ b a s e i hs(s)=\sum\limits_{i=1}^n(s_i-'0'+1)*base^i hs(s)=i=1∑n​(si​−′0′+1)∗basei
定义 f ( x , y ) f(x,y) f(x,y)为 ( x , y ) (x,y) (x,y)的哈希值。对于每一个结点 u u u,记录 f ( 1 , u ) f(1,u) f(1,u)和 f ( u , 1 ) f(u,1) f(u,1)。设点 s s s为 t t t的祖先,则 f ( s , t ) = ( f ( 1 , t ) − f ( 1 , f a s ) ) ∗ i n v ( b a s e d e p s − 1 ) f(s,t)=(f(1,t)-f(1,fa_s))*inv(base^{dep_s-1}) f(s,t)=(f(1,t)−f(1,fas​))∗inv(basedeps​−1) f ( t , s ) = f ( t , 1 ) − b a s e d e p t − d e p s ∗ f ( s , 1 ) f(t,s)=f(t,1)-base^{dep_t-dep_s}*f(s,1) f(t,s)=f(t,1)−basedept​−deps​∗f(s,1)同时,若 s s s和 t t t互不为祖先,求出其 l c a lca lca拆分出两条路径并采用,仍然可用上述方法 O ( 1 ) O(1) O(1)求出哈希值。
从而,对于每个查询,求出 l c a ( a , b ) lca(a,b) lca(a,b)和 l c a ( c , d ) lca(c,d) lca(c,d),二分 ( a , b ) (a,b) (a,b)和 ( c , d ) (c,d) (c,d)最长公共前缀的长度,长链剖分求 b b b和 d d d的 k k k级祖先,判断哈希值是否相等即可,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)。

CF505E Mr. Kitayuta vs. Bamboos

有 n n n朵花,第 i i i朵花最初高度为 h i h_i hi​,每天晚上长高 a i a_i ai​。每天白天可以使用 k k k次魔法,魔法选择一朵花 i i i,将其高度变为 m a x ( 0 , h i − p ) max(0,h_i-p) max(0,hi​−p)。 m m m天后所有花中的最大高度最小是多少?
n ≤ 100000 ; m ≤ 5000 ; k ≤ 10 ; p , a i , h i ≤ 1 e 9 n\le100000;m\le5000;k\le10;p,a_i,h_i\le1e9 n≤100000;m≤5000;k≤10;p,ai​,hi​≤1e9

二分 m m m天后花的最大高度 H H H,判断 m m m天后花的高度是否都 ≤ H \le H ≤H。由 h i = m a x ( 0 , h i − p ) h_i=max(0,h_i-p) hi​=max(0,hi​−p)的限制,在 h i ≤ p h_i\le p hi​≤p时,会浪费 p − h i p-h_i p−hi​的长度,因此需要考虑尽可能少的浪费长度。
时间倒流问题转换:有 n n n朵花,第 i i i朵花最初高度为 H H H,每天白天长矮 a i a_i ai​。每天晚上可以使用 k k k次魔法,魔法选择一朵花 i i i,将其高度增加 p p p。 m m m天后花的高度是否大于 h i h_i hi​?过程中花的高度要 ≥ 0 \ge0 ≥0。每次贪心选择最快会长矮为负数的花使用魔法长高即可。
实际上,在时间顺流中花不会从一个负高度长到正高度,因此时间倒流中花不可以长矮到负距离,而倒流中魔法没有浪费,从而得到最优策略,问题可以这样转换的关键是魔法可以不浪费。

arc111F Do you like query problems?

a n s = 期 望 值 ∗ 方 案 数 ans=期望值*方案数 ans=期望值∗方案数,因此只计算其期望 E E E即可。
对于一个固定的位置 i i i,其被操作的概率为 P i = i ∗ ( n − i + 1 ) ∗ 2 n ∗ ( n + 1 ) P_i=\frac{i*(n-i+1)*2}{n*(n+1)} Pi​=n∗(n+1)i∗(n−i+1)∗2​,只需解决一下问题即可:
给定两个数 x , s u m i x,sum_i x,sumi​初始都为 0 0 0,随机进行 q q q次操作,每次操作:

  1. 有 1 − P i 1-P_i 1−Pi​概率什么都不做
  2. 有 P i ∗ 1 2 ∗ m + 1 P_i*\frac{1}{2*m+1} Pi​∗2∗m+11​的概率使得 s u m i = s u m i + x sum_i=sum_i+x sumi​=sumi​+x
  3. 有 P i ∗ 1 2 ∗ m + 1 P_i*\frac{1}{2*m+1} Pi​∗2∗m+11​的概率使得 x = m i n ( x , v ) ( v ∈ { 0 , 1 , . . . , m − 1 } ) x=min(x,v)(v\in\{0,1,...,m-1\}) x=min(x,v)(v∈{0,1,...,m−1})
  4. 有 P i ∗ 1 2 ∗ m + 1 P_i*\frac{1}{2*m+1} Pi​∗2∗m+11​的概率使得 x = m a x ( x , v ) ( v ∈ { 0 , 1 , . . . , m − 1 } ) x=max(x,v)(v\in\{0,1,...,m-1\}) x=max(x,v)(v∈{0,1,...,m−1})

求 s u m i sum_i sumi​的期望值是多少。
设 p j , v p_{j,v} pj,v​为第 j j j次操作后 x = v x=v x=v的概率。 p j , ≥ v p_{j,\ge v} pj,≥v​为第 j j j次操作后 x ≥ v x\ge v x≥v的概率。则 s u m i = ∑ j = 0 q − 1 ∑ v = 0 m − 1 p j , v ∗ v ∗ P i ∗ 1 2 ∗ m + 1 = ∑ j = 0 q − 1 ∑ v = 1 m − 1 p j , ≥ v ∗ P i ∗ 1 2 ∗ m + 1 p j + 1 , ≥ v = p j , ≥ v ∗ ( 1 − P i ) + P i ∗ p j , ≥ v ∗ m + 1 + m − v 2 ∗ m + 1 + P i ∗ ( 1 − p j , ≥ v ) ∗ m − v 2 ∗ m + 1 = p j , ≥ v ∗ ( 1 − m 2 ∗ m + 1 ∗ P i ) + P i ∗ m − v 2 ∗ m + 1 sum_i=\sum\limits_{j=0}^{q-1}\sum\limits_{v=0}^{m-1}p_{j,v}*v*P_i*\frac{1}{2*m+1}\\=\sum\limits_{j=0}^{q-1}\sum\limits_{v=1}^{m-1}p_{j,\ge v}*P_i*\frac{1}{2*m+1}\\p_{j+1,\ge v}=p_{j,\ge v}*(1-P_i)+P_i*p_{j,\ge v}*\frac{m+1+m-v}{2*m+1}+\\ P_i*(1-p_{j,\ge v})*\frac{m-v}{2*m+1}=\\p_{j,\ge v}*(1-\frac{m}{2*m+1}*P_i)+P_i*\frac{m-v}{2*m+1} sumi​=j=0∑q−1​v=0∑m−1​pj,v​∗v∗Pi​∗2∗m+11​=j=0∑q−1​v=1∑m−1​pj,≥v​∗Pi​∗2∗m+11​pj+1,≥v​=pj,≥v​∗(1−Pi​)+Pi​∗pj,≥v​∗2∗m+1m+1+m−v​+Pi​∗(1−pj,≥v​)∗2∗m+1m−v​=pj,≥v​∗(1−2∗m+1m​∗Pi​)+Pi​∗2∗m+1m−v​
令 a i = 1 − m 2 ∗ m + 1 ∗ P i , b v = m − v 2 ∗ m + 1 a_i=1-\frac{m}{2*m+1}*P_i,b_v=\frac{m-v}{2*m+1} ai​=1−2∗m+1m​∗Pi​,bv​=2∗m+1m−v​,可以得到 p 0 , ≥ v = 0 ( v ≥ 1 ) p j + 1 , ≥ v = p j , ≥ v ∗ a i + P i ∗ b v p_{0,\ge v}=0(v\ge 1)\\ p_{j+1,\ge v}=p_{j,\ge v}*a_i+P_i*b_v p0,≥v​=0(v≥1)pj+1,≥v​=pj,≥v​∗ai​+Pi​∗bv​
观察该式子可以发现 p j , ≥ v = P i ∗ b v ∗ ∑ k = 0 j − 1 a i j ( j ≥ 1 ) p_{j,\ge v}=P_i*b_v*\sum\limits_{k=0}^{j-1}a_i^j(j\ge 1) pj,≥v​=Pi​∗bv​∗k=0∑j−1​aij​(j≥1)。
则可以得到 E = ∑ i = 1 n s u m i = ∑ i = 1 n ∑ v = 1 m − 1 ∑ j = 1 q − 1 ∗ P i ∗ 1 2 ∗ m + 1 P i ∗ b v ∗ ∑ k = 0 j − 1 a i j = 1 2 ∗ m + 1 ∗ ( ∑ v = 1 m − 1 b v ) ∗ ∑ i = 1 n P i 2 ( ∑ j = 1 q − 1 ∑ k = 0 j − 1 a i j ) E=\sum_{i=1}^nsum_i=\sum_{i=1}^n\sum_{v=1}^{m-1}\sum_{j=1}^{q-1}*P_i*\frac{1}{2*m+1}P_i*b_v*\sum_{k=0}^{j-1}a_i^j\\=\frac{1}{2*m+1}*(\sum_{v=1}^{m-1}b_v)*\sum_{i=1}^nP_i^2(\sum_{j=1}^{q-1}\sum_{k=0}^{j-1}a_i^j) E=i=1∑n​sumi​=i=1∑n​v=1∑m−1​j=1∑q−1​∗Pi​∗2∗m+11​Pi​∗bv​∗k=0∑j−1​aij​=2∗m+11​∗(v=1∑m−1​bv​)∗i=1∑n​Pi2​(j=1∑q−1​k=0∑j−1​aij​)
对 ( ∑ j = 1 q − 1 ∑ k = 0 j − 1 a i j ) (\sum_{j=1}^{q-1}\sum_{k=0}^{j-1}a_i^j) (∑j=1q−1​∑k=0j−1​aij​)可以设计矩阵快速幂 O ( l o g n ) O(logn) O(logn)计算,或者经过数学推导可以证明: ( ∑ j = 1 q − 1 ∑ k = 0 j − 1 a i j ) = q − 1 − a i ( 1 − a i q − 1 ) 1 − a i 1 − a i (\sum_{j=1}^{q-1}\sum_{k=0}^{j-1}a_i^j)=\frac{q-1-\frac{a_i(1-a_i^{q-1})}{1-a_i}}{1-a_i} (j=1∑q−1​k=0∑j−1​aij​)=1−ai​q−1−1−ai​ai​(1−aiq−1​)​​
从而整个过程只需要枚举 i i i,时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)。
最后答案为 E ∗ ( n ∗ ( n + 1 ) 2 ∗ ( 2 ∗ m + 1 ) ) q E*(\frac{n*(n+1)}{2}*(2*m+1))^q E∗(2n∗(n+1)​∗(2∗m+1))q

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=4e5+5,mod=998244353;
int n,m,q;
ll inv[N];
ll qpow(ll a,ll n)
{
    ll ans=1;
    for(;n;n>>=1,a=a*a%mod)
        if(n&1) ans=ans*a%mod;
    return ans;
}
struct node
{
    int n,m;
    node(int n=0,int m=0):n(n),m(m){memset(a,0,sizeof(a));}
    ll a[4][4];
    node operator*(const node&o)const
    {
        node ans(n,o.m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=o.m;j++)
            for(int k=1;k<=m;k++)
            ans.a[i][j]=(ans.a[i][j]+a[i][k]*o.a[k][j])%mod;
        return ans;
    }
};
node qpow(node a,ll n)
{
    node ans(a.n,a.n);
    for(int i=1;i<=a.n;i++) ans.a[i][i]=1;
    for(;n;n>>=1,a=a*a)
        if(n&1) ans=ans*a;
    return ans;
}
int main()
{
    inv[1]=1;for(int i=2;i<N;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    cin>>n>>m>>q;
    ll ans=qpow(1ll*n*(n+1)/2%mod*(2*m%mod+1)%mod,q)%mod;
    ll sum=0;
    for(int v=1;v<m;v++)
        sum=(sum+(m-v)%mod*inv[2*m+1])%mod;
    ans=ans*sum%mod*inv[2*m+1]%mod;
    ll E=0;
    for(int i=1;i<=n;i++)
    {
        ll p=1ll*i*(n-i+1)%mod*inv[n]%mod*inv[n+1]%mod*2%mod;
        ll a=1-m*inv[2*m+1]%mod*p%mod;
        a=(a%mod+mod)%mod;
        //node A(3,3);
        //A.a[1][1]=A.a[2][1]=A.a[2][2]=A.a[3][2]=1;
        //A.a[3][3]=a;A=qpow(A,q-1);
        //sum=(A.a[2][1]+a*A.a[3][1]%mod)%mod;
        ll s=qpow((1+mod-a)%mod,mod-2);
        sum=q-1-a*(1-qpow(a,q-1))%mod*s%mod;
        sum=(sum%mod+mod)%mod;
        sum=sum*s%mod;
        E=(E+sum*p%mod*p%mod)%mod;
    }
    ans=ans*E%mod;
    printf("%lld\n",ans);
}

ACL1F Center Rearranging

可以将 B B B序列划分成 L . . . L M . . . M R . . . R L...LM...MR...R L...LM...MR...R, L L L表示该位置的数是通过操作移动到 B B B序列的左边的, R R R表示该位置的数是通过操作移动到 B B B序列的左右边的, M M M表示该位置的数没有被移动过。这样的划分共有 O ( n 2 ) O(n^2) O(n2)种。一种划分只要合法就可以在 3 ∗ n − ∣ M ∣ 3*n-|M| 3∗n−∣M∣步内得出(归纳法可证)。
固定一种划分,一个数 x x x在划分序列中的状态有 10 10 10种可能:

  1. L L L LLL LLL:三个数都被往左移动(最右边的 x x x无法往左移动),这种序列是不可能的。
  2. R R R RRR RRR:三个数都被往右移动,同样这种序列也不可能实现。
  3. L M M LMM LMM:有一个 x x x被往左移动,其余两个未被移动,这种情况只能最中间的 x x x往左移动一次。
  4. M M R MMR MMR:只能最中间的 x x x往右移动一次。
  5. L L M LLM LLM:设三个 x x x从左到右分别为 x 1 x 2 x 3 x_1x_2x_3 x1​x2​x3​,先移动 x 2 x_2 x2​,再移动 x 1 x_1 x1​(事实上再移动 x 2 x_2 x2​是没有必要的)。
  6. M R R MRR MRR:先移动 x 2 x_2 x2​再移动 x 3 x_3 x3​。
  7. M M M MMM MMM:三个 x x x都未被移动过。
  8. L L R LLR LLR:先 x 2 x_2 x2​向右移动,再 x 3 , x 1 x3,x1 x3,x1向左移动。
  9. L R R LRR LRR:先 x 2 x_2 x2​向左移动,再 x 1 , x 3 x1,x3 x1,x3向右移动。
  10. L M R LMR LMR :先 x 2 x_2 x2​向左再 x 1 x_1 x1​向右或者先 x 2 x_2 x2​向右再 x 3 x_3 x3​向左。

从上述 10 10 10种情况可以发现,除了 L M R LMR LMR的情况, A A A中的哪个 x x x对应 B B B中哪个位置都是可以确定的。假设 L M R LMR LMR也已确定,则在 A A A中的 x x x和它对应的 B B B的 M M M之间连一条边,由于这些元素都没有移动过,需要满足这些边之间没有交叉。 L M R LMR LMR没确定可以用 2 − s a t 2-sat 2−sat解决。 ( C o n d i t i o n 1 ) (Condition1) (Condition1)

事实上,如果 L M R LMR LMR也确定了,可以用以下方法还原:
最开始 C C C序列为 M M M MMM MMM,选择一个可移动的 L L L或 R R R,填加到 C C C的两边。
这也说明了最多只需要 n ∗ 3 − ∣ M ∣ n*3-|M| n∗3−∣M∣步可以完成。

其次, L . . . L M . . . M R . . . R L...LM...MR...R L...LM...MR...R序列的还原具有顺序性,我们在 B B B序列中 L i + 1 L_{i+1} Li+1​向 L i L_i Li​连一条有向边(表示还原了 L i L_i Li​才能还原 L i + 1 L_{i+1} Li+1​), R i R_i Ri​向 R i + 1 R_{i+1} Ri+1​连一条有向边,在 5 , 6 , 8 , 9 , 10 5,6,8,9,10 5,6,8,9,10中, x x x的移动也有顺序,根据 x x x的移动顺序又需要加上一些限制还原顺序的边。如 5 5 5中先移动 x 2 x_2 x2​才能移动 x 1 x_1 x1​,设 x 1 , x 2 x_1,x_2 x1​,x2​分别还原到序列中的 L k 1 , L k 2 L_{k_1},L_{k_2} Lk1​​,Lk2​​,则 L k 2 L_{k_2} Lk2​​向 L k 1 L_{k_1} Lk1​​连一条有向边。则需要保证,在这张图中没有环。 ( C o n d i t i o n 2 ) (Condition2) (Condition2)

However, ( C o n d i t i o n 1 ) (Condition1) (Condition1)和 ( C o n d i t i o n 2 ) (Condition2) (Condition2)使用不同的判定方法,仍然无法判顶合法性。在继续观察,如果这样的图中存在环,则必然有这样的环: L i → R j → R ≥ j → L ≥ i → L i L_i\rightarrow R_j\rightarrow R_{\ge j}\rightarrow L_{\ge i}\rightarrow L_i Li​→Rj​→R≥j​→L≥i​→Li​或 R j → L i → L ≤ i → R ≤ j → R j R_j\rightarrow L_i\rightarrow L_{\le i}\rightarrow R_{\le j}\rightarrow R_j Rj​→Li​→L≤i​→R≤j​→Rj​。没换的条件可以改为:
若有 L x → R y L_x\rightarrow R_y Lx​→Ry​的边,则不能有 R ≥ y → L ≥ x R_{\ge y}\rightarrow L_{\ge x} R≥y​→L≥x​的边。
若有 R y → L x R_y\rightarrow L_x Ry​→Lx​的边,则不能有 L ≤ x → R ≤ y L_{\le x}\rightarrow R_{\le y} L≤x​→R≤y​的边。
从而条件也转换为 2 − s a t 2-sat 2−sat的判定问题。
− _ − -\_- −_−不愧难度为3700的题,即难想又难写。

#include<bits/stdc++.h>
using namespace std;
const int N=2e6+5;
int n,m,tot,head[N],nex[N<<1],to[N<<1];
void add(int u,int v){to[++tot]=v;nex[tot]=head[u];head[u]=tot;}
bool vis[N<<1];
int top,s[N<<1];
bool dfs(int x)
{
    if(vis[x^1]) return false;
    if(vis[x]) return true;
    vis[x]=true;s[++top]=x;
    for(int i=head[x];i;i=nex[i])
        if(!dfs(to[i])) return false;
    return true;
}
int _n,A[N],B[N];
vector<int>v1[40],v2[40];
char get(int l,int r,int y)
{
    if(y>=l&&y<=r) return 'M';
    if(y<l) return 'L';
    return 'R';
}
vector<pair<int,int>>e1,e2,e3;
pair<int,int>gete(int x,int l,int r)
{
    return {v1[x][l-1],v2[x][r-1]};
}
pair<int,int>gett(int x,int l,int r)
{
    return {v2[x][l-1],v2[x][r-1]};
}
void add(int x,int a,int y,int b)
{
    x<<=1;y<<=1;
    add(x^a,y^b);
}
bool jiao(pair<int,int>a,pair<int,int>b)
{
    if(a.first==b.first||a.second==b.second) return true;
    return a.first>b.first&&a.second<b.second||a.first<b.first&&a.second>b.second;
}
bool biu(pair<int,int>x,pair<int,int>y)
{
    if(x.first<x.second&&y.first>y.second&&y.first>=x.second&&y.second>=x.first) return true;
    if(x.first>x.second&&y.first<y.second&&y.first<=x.second&&y.second<=x.first) return true;
    swap(x,y);
    if(x.first<x.second&&y.first>y.second&&y.first>=x.second&&y.second>=x.first) return true;
    if(x.first>x.second&&y.first<y.second&&y.first<=x.second&&y.second<=x.first) return true;
    return false;
}
bool judge(int l,int r)
{
    e1.clear();e2.clear();e3.clear();
    for(int x=1;x<=_n;x++)
    {
        string s="";
        for(int y:v2[x])
            s+=get(l,r,y);
        if(s=="LLL"||s=="RRR") return false;
        if(s=="LMM")
            e1.push_back(gete(x,1,2)),e1.push_back(gete(x,3,3));
        if(s=="MMR")
            e1.push_back(gete(x,1,1)),e1.push_back(gete(x,3,2));
        if(s=="LLM")
            e1.push_back(gete(x,3,3));
        if(s=="MRR")
            e1.push_back(gete(x,1,1));
        if(s=="MMM")
            e1.push_back(gete(x,1,1)),e1.push_back(gete(x,2,2)),e1.push_back(gete(x,3,3));
        if(s=="LLR")
            e2.push_back(gett(x,3,1));
        if(s=="LRR")
            e2.push_back(gett(x,1,3));
        if(s=="LMR")
        {
            /*
            if get(x,1,2) then gett(3,1)
            if get(x,3,2) then gett(1,3)
            */
            e3.push_back(gete(x,1,2));e3.push_back(gete(x,3,2));
            e3.push_back(gett(x,3,1));e3.push_back(gett(x,1,3));
        }
    }
    n=e1.size()+e2.size()+e3.size()+2;
    int n1=e1.size(),n2=e1.size()+e2.size();
    tot=0;
    for(int i=1;i<=n*3;i++) head[i]=vis[i]=0;
    add(n+1,0,n+2,0);add(n+2,0,n+1,1);
    for(int i=1;i<=n1;i++)
    {
        add(i,0,n+1,0);
        for(int j=1;j<=n1;j++)
            if(i!=j&&jiao(e1[i-1],e1[j-1]))
            add(i,1,j,0);
    }
    for(int i=1;i<=e2.size();i++)
    {
        add(i+n1,0,n+1,0);
        for(int j=1;j<=e2.size();j++)
            if(i!=j&&biu(e2[i-1],e2[j-1]))
            add(i+n1,1,j+n1,0);
    }
    for(int i=0;i<e3.size();i+=4)
    {
        add(i+1+n2,0,i+2+n2,1);
        add(i+2+n2,0,i+1+n2,1);
        add(i+1+n2,1,i+2+n2,0);
        add(i+2+n2,1,i+1+n2,0);
        add(i+1+n2,1,i+3+n2,1);
        add(i+1+n2,1,i+4+n2,0);
        add(i+2+n2,1,i+3+n2,0);
        add(i+2+n2,1,i+4+n2,1);
        for(int j=1;j<=n1;j++)
        {
            if(jiao(e1[j-1],e3[i]))
            {
                add(i+1+n2,1,j,0);
                add(j,1,i+1+n2,0);
            }
            if(jiao(e1[j-1],e3[i+1]))
            {
                add(i+2+n2,1,j,0);
                add(j,1,i+2+n2,0);
            }
        }
        for(int j=1;j<=e2.size();j++)
        {
            if(biu(e2[j-1],e3[i+2]))
            {
                add(i+3+n2,1,j+n1,0);
                add(j+n1,1,i+3+n2,0);
            }
            if(biu(e2[j-1],e3[i+3]))
            {
                add(i+4+n2,1,j+n1,0);
                add(j+n1,1,i+4+n2,0);
            }
        }
        for(int j=0;j<e3.size();j+=4)
            if(i!=j)
        {
            if(jiao(e3[i],e3[j]))
                add(i+1+n2,1,j+1+n2,0);
            if(jiao(e3[i],e3[j+1]))
                add(i+1+n2,1,j+2+n2,0);
            if(jiao(e3[i+1],e3[j]))
                add(i+2+n2,1,j+1+n2,0);
            if(jiao(e3[i+1],e3[j+1]))
                add(i+2+n2,1,j+2+n2,0);
            if(biu(e3[i+2],e3[j+2]))
                add(i+1+2+n2,1,j+1+2+n2,0);
            if(biu(e3[i+2],e3[j+1+2]))
                add(i+1+2+n2,1,j+2+2+n2,0);
            if(biu(e3[i+1+2],e3[j+2]))
                add(i+2+n2+2,1,j+1+n2+2,0);
            if(biu(e3[i+1+2],e3[j+1+2]))
                add(i+2+2+n2,1,j+2+2+n2,0);
        }
    }
    bool flag=true;
    for(int i=1;i<=n;i++)
        if(!vis[i<<1]&&!vis[i<<1|1])
    {
        top=0;
        if(!dfs(i<<1))
        {
            while(top) vis[s[top--]]=false;
            if(!dfs(i<<1|1)) { flag=false;break; }
        }
    }
    return flag;
}
int main()
{
    cin>>_n;
    for(int i=1;i<=_n*3;i++) cin>>A[i],v1[A[i]].push_back(i);
    for(int i=1;i<=_n*3;i++) cin>>B[i],v2[B[i]].push_back(i);
    int ans=_n*10;
    for(int i=1;i<=_n*3;i++)
        for(int j=i;j<=_n*3;j++)
        if(judge(i,j)) ans=min(ans,_n*3-(j-i+1));
    printf("%d\n",ans==_n*10?-1:ans);
}

abc182F Valid payments

一个数 X X X可以表示为 X = a 1 ∗ k 1 + a 2 ∗ k 2 + . . . + a n ∗ k n X=a_1*k_1+a_2*k_2+...+a_n*k_n X=a1​∗k1​+a2​∗k2​+...+an​∗kn​,满足 k i < a i + 1 a i ( 1 ≤ k < n ) , k n ≤ i n f k_i<\frac{a_{i+1}}{a_i}(1\le k<n),k_n\le inf ki​<ai​ai+1​​(1≤k<n),kn​≤inf,第 i i i个数达到 a i + 1 a i \frac{a_{i+1}}{a_i} ai​ai+1​​就会发生进位。不妨把 Z Z Z称为怪进制数。则问题等价于求: Y − Z = X Y-Z=X Y−Z=X且 Y & Z = 0 Y\& Z=0 Y&Z=0(这里的 & \& &运算把 k i > 0 k_i>0 ki​>0看作 1 1 1)的 Y Y Y的数量。
实际上,对于一个 Y Y Y, Z Z Z用剩余的数的怪进制表示是唯一的(要求用尽可能少数量的个数来表示 Z Z Z)。
令 b i = a i + 1 a i ( 1 ≤ i < n ) , b n = i n f b_i=\frac{a_{i+1}}{a_i}(1\le i<n),b_n=inf bi​=ai​ai+1​​(1≤i<n),bn​=inf
不妨把问题看为:对于一个怪进制表示的 X X X,存在多少不同的 Z Z Z,使得, ( X + Z ) & Z = 0 (X+Z)\& Z=0 (X+Z)&Z=0。令 Z = a 1 ∗ s 1 + a 2 ∗ s 2 + . . . + a n ∗ s n Z=a_1*s_1+a_2*s_2+...+a_n*s_n Z=a1​∗s1​+a2​∗s2​+...+an​∗sn​,这样的 Z Z Z满足:
若 k i > 1 k_i>1 ki​>1且 s i > 1 s_i>1 si​>1,则必然有 s i = b i − k i s_i=b_i-k_i si​=bi​−ki​。
若 k i = 0 k_i=0 ki​=0则 s i = 0 s_i=0 si​=0。
从而,设 f i , 0 / 1 f_{i,0/1} fi,0/1​为 Z Z Z为在怪进制表示下 s i = 0 / s i > 0 s_i=0/s_i>0 si​=0/si​>0的方案数,推导转移即可。
此外,注意怪进制的第 n n n位是不可以进位的,故 s n s_n sn​必然始终为 0 0 0。

#include<bits/stdc++.h>
using namespace std;
const int N=55;
typedef long long ll;
int n;
ll x,k[N],a[N],b[N],f[N][2];
int main()
{
    cin>>n>>x;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<n;i++) b[i]=a[i+1]/a[i];
    for(int i=n;i>=1;i--)
    {
        k[i]=x/a[i];
        x%=a[i];
    }
    if(n==1)
    {
        printf("1\n");return 0;
    }
    f[1][0]=1;
    if(k[1]!=0) f[1][1]=1;
    for(int i=2;i<n;i++)
    {
        f[i][0]=f[i-1][0];
        if(k[i]!=0) f[i][1]=f[i-1][0];
        if(k[i]!=b[i]-1)
            f[i][0]+=f[i-1][1];
        f[i][1]+=f[i-1][1];
    }
    printf("%lld\n",f[n-1][0]+f[n-1][1]);
}

abc181F Silver Woods

设圆的直径为 X X X,则把距离 < X <X <X的点与点之间,点与 y = 100 y=100 y=100和 y = − 100 y=-100 y=−100之间连一条边,只要最后 y = 100 y=100 y=100和 y = − 100 y=-100 y=−100不在同一个连通分量即可。可以二分 X X X,然后并查集判断。或者将所有边按长度排序,然后逐个条边加入并查集即可。

#include<bits/stdc++.h>
using namespace std;
const int N=105;
typedef long long ll;
const double eps=1e-8;
double x[N],y[N];
int n,f[N];
struct node
{
    int x,y;
    double dis;
    bool operator<(const node&o)const
    {
        return dis<o.dis;
    }
};
vector<node>e;
double dis(double x1,double y1,double x2,double y2)
{
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        e.push_back({i,j,dis(x[i],y[i],x[j],y[j])});
    for(int i=1;i<=n;i++)
    {
        e.push_back({i,n+1,abs(y[i]-100)});
        e.push_back({i,n+2,abs(y[i]+100)});
    }
    sort(e.begin(),e.end());
    for(int i=1;i<=n+2;i++) f[i]=i;
    double ans=0;
    for(int i=0;i<e.size();i++)
    {
        int j=i;
        while(j<e.size()&&fabs(e[j].dis-e[i].dis)<eps)
        {
            int u=e[j].x,v=e[j].y;
            if(getf(u)!=getf(v))
                f[getf(u)]=getf(v);
            if(getf(n+1)==getf(n+2)) break;
            j++;
        }
        ans=e[i].dis;i=j-1;
        if(getf(n+1)==getf(n+2)) break;
    }
    printf("%.10f\n",ans/2);
}

abc180F Unbranched

设 f i , j , l f_{i,j,l} fi,j,l​为 i i i个点 m m m条边,最大连通分量里点数 ≤ l \le l ≤l的方案数。
答案为 f i , j , l − f i , j , l − 1 f_{i,j,l}-f_{i,j,l-1} fi,j,l​−fi,j,l−1​。
度数为 2 2 2,则一个连通块为链或者为环。
当 n > 2 n>2 n>2, n n n个点成链的方案数为 n ! 2 \frac{n!}{2} 2n!​,成环为 ( n − 1 ) ! 2 \frac{(n-1)!}{2} 2(n−1)!​。
此外,连通分量只和集合有关,和顺序无法,从而从 n n n个点中拿出 k k k个点成为一个连通分量的方案数为 C ( n − 1 , k − 1 ) C(n-1,k-1) C(n−1,k−1)。含义为:
从点 2 , 3 , . . . , n − 1 2,3,...,n-1 2,3,...,n−1中选择 k − 1 k-1 k−1个点,与点 1 1 1连成一个连通分量。
从而可以推出转移方程。

#include<bits/stdc++.h>
using namespace std;
const int N=305,mod=1e9+7;
typedef long long ll;
ll f[N][N],c[N][N],p[N];
ll C(ll n,ll m)
{
    if(m==0||m==n) return 1;
    if(m==1) return n;
    if(c[n][m]) return c[n][m];
    return c[n][m]=(C(n-1,m-1)+C(n-1,m))%mod;
}
void update(ll &x,ll y)
{
    x=(x+y)%mod;
}
int n,m,l;
ll DP(int n,int m,int l)
{
    memset(f,0,sizeof(f));
    f[0][0]=1;
    for(int i=0;i<=n;i++)
        for(int j=0;j<=m;j++)
    {
        update(f[i+1][j],f[i][j]);
        if(l>=2&&i+2<=n&&j+1<=m) update(f[i+2][j+1],f[i][j]*(n-i-1));
        if(l>=2&&i+2<=n&&j+2<=m) update(f[i+2][j+2],f[i][j]*(n-i-1));
        for(int k=3;k+i<=n&&k<=l;k++)
        {
            if(j+k-1<=n)
                update(f[i+k][j+k-1],f[i][j]*C(n-i-1,k-1)%mod*p[k]%mod*(mod-mod/2)%mod);
            if(j+k<=n)
                update(f[i+k][j+k],f[i][j]*C(n-i-1,k-1)%mod*p[k-1]%mod*(mod-mod/2)%mod);
        }
    }
    return f[n][m];
}
int main()
{
    p[0]=1;for(int i=1;i<N;i++) p[i]=i*p[i-1]%mod;
    scanf("%d%d%d",&n,&m,&l);
    printf("%lld\n",(DP(n,m,l)+mod-DP(n,m,l-1))%mod);
}

标签:初阵,进击,sum,int,ans,n2,刷题,ll,mod
来源: https://blog.csdn.net/Huah_2018/article/details/113833607