进击的刷题人 第5话 初阵
作者:互联网
文章目录
- ACL1C Moving Pieces
- ACL1D Keep Distances
- ACL1E Shuffle Window
- CF504E Misha and LCP on Tree
- CF505E Mr. Kitayuta vs. Bamboos
- arc111F Do you like query problems?
- ACL1F Center Rearranging
- abc182F Valid payments
- abc181F Silver Woods
- abc180F Unbranched
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∑ni=1∑j−12[pi<pj](kk−1)tj−ti+[pi>pj](1−(kk−1)tj−ti)=(j=1∑n2(kk−1)tji=1∑j−1[pi<pj](kk−1)−ti)+{j=1∑n(i=1∑j−1[pi>pj])−(kk−1)tji=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 − P i 1-P_i 1−Pi概率什么都不做
- 有 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
- 有 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})
- 有 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−1v=0∑m−1pj,v∗v∗Pi∗2∗m+11=j=0∑q−1v=1∑m−1pj,≥v∗Pi∗2∗m+11pj+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−1aij(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∑nsumi=i=1∑nv=1∑m−1j=1∑q−1∗Pi∗2∗m+11Pi∗bv∗k=0∑j−1aij=2∗m+11∗(v=1∑m−1bv)∗i=1∑nPi2(j=1∑q−1k=0∑j−1aij)
对
(
∑
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−1aij)可以设计矩阵快速幂
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−1k=0∑j−1aij)=1−aiq−1−1−aiai(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种可能:
- L L L LLL LLL:三个数都被往左移动(最右边的 x x x无法往左移动),这种序列是不可能的。
- R R R RRR RRR:三个数都被往右移动,同样这种序列也不可能实现。
- L M M LMM LMM:有一个 x x x被往左移动,其余两个未被移动,这种情况只能最中间的 x x x往左移动一次。
- M M R MMR MMR:只能最中间的 x x x往右移动一次。
- L L M LLM LLM:设三个 x x x从左到右分别为 x 1 x 2 x 3 x_1x_2x_3 x1x2x3,先移动 x 2 x_2 x2,再移动 x 1 x_1 x1(事实上再移动 x 2 x_2 x2是没有必要的)。
- M R R MRR MRR:先移动 x 2 x_2 x2再移动 x 3 x_3 x3。
- M M M MMM MMM:三个 x x x都未被移动过。
- L L R LLR LLR:先 x 2 x_2 x2向右移动,再 x 3 , x 1 x3,x1 x3,x1向左移动。
- L R R LRR LRR:先 x 2 x_2 x2向左移动,再 x 1 , x 3 x1,x3 x1,x3向右移动。
- 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<aiai+1(1≤k<n),kn≤inf,第
i
i
i个数达到
a
i
+
1
a
i
\frac{a_{i+1}}{a_i}
aiai+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=aiai+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