传送门:cf520
A.Prank
数组前面塞一个0,后面塞一个1001,答案就是 m a x ( ∣ 最 长 连 续 子 序 列 ∣ − 2 , 0 ) max(|最长连续子序列|-2,0) max(∣最长连续子序列∣−2,0)
#include<bits/stdc++.h>
using namespace std;
int n,a[1010],ans;
int main(){
int i,j,k;
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
a[0]=0;a[n+1]=1001;n++;
k=0;
for(i=0;i<=n;++i){
if(a[i]==a[i-1]+1){
k++;
}else k=1;
ans=max(ans,k-2);
}
printf("%d\n",ans);
return 0;
}
B.Math
最小值必然是
n
n
n的所有质因数的一次幂相乘。
设
n
=
∏
i
=
1
m
p
i
k
i
n=\prod\limits_{i=1}^mp_i^{k_i}
n=i=1∏mpiki
找到最小的
t
t
t使得
2
t
≥
m
a
x
(
k
i
)
2^t\geq max(k_i)
2t≥max(ki)
贪心一次性将
n
n
n转成
∏
i
=
1
m
p
i
2
t
\prod\limits_{i=1}^mp_i^{2^t}
i=1∏mpi2t,最后开
t
t
t次根就好了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
int n,m,mx,p[N],cot;
ll ans;
inline int F(int x)
{
int i;
for(i=0;(1<<i)<x;++i);
return i;
}
int main(){
int i,j,k;m=1;
scanf("%d",&n);
for(i=2;(ll)i*i<=n;++i) if(n%i==0){
for(j=0;n%i==0;n/=i) ++j;
p[++cot]=j;mx=max(F(j),mx);
m*=i;
}
m*=n;
if(n>1) p[++cot]=1;
for(i=1;i<=cot;++i) mx=max(mx,(F(p[i])));
for(i=1;i<=cot;++i)
if(p[i]!=(1<<mx)){mx++;break;}
printf("%d %I64d",m,mx);
return 0;
}
C.Banh-mi
对于每个区间,贪心先选所有的
1
1
1。
设区间
[
l
,
r
]
[l,r]
[l,r],长度
l
e
n
=
r
−
l
+
1
len=r-l+1
len=r−l+1,其中有
x
x
x个1。
依次取1时构成等比数列:
1
,
2
,
4
,
.
.
.
,
2
x
−
1
1,2,4,...,2^{x-1}
1,2,4,...,2x−1
依次取0时构成等比数列:
2
x
−
1
,
2
(
2
x
−
1
)
,
4
(
2
x
−
1
)
.
.
.
2^x-1,2(2^x-1),4(2^x-1)...
2x−1,2(2x−1),4(2x−1)...
总共为
(
2
x
−
1
)
2
l
e
n
−
x
(2^x-1)2^{len-x}
(2x−1)2len−x,前缀和处理一下1的个数
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10,mod=1e9+7;
int bin[N],s[N],n,m;
char t[N];
int main(){
int i,j,k,l,r;
scanf("%d%d%s",&n,&m,t+1);
for(i=1;i<=n;++i) s[i]=s[i-1]+(t[i]=='1');
bin[0]=1;
for(i=1;i<=n;++i) bin[i]=(ll)bin[i-1]*2%mod;
for(;m;--m){
scanf("%d%d",&l,&r);
j=s[r]-s[l-1];
printf("%d\n",(ll)((bin[j]-1+mod)%mod*(ll)bin[r-l+1-j])%mod);
}
return 0;
}
D.Fun with Integers
实际上找出所有二元组
(
∣
a
∣
,
∣
b
∣
)
(|a|,|b|)
(∣a∣,∣b∣)即可。
设
∣
a
∣
<
∣
b
∣
|a|<|b|
∣a∣<∣b∣,对于
∣
a
∣
|a|
∣a∣所有可行的
∣
b
∣
|b|
∣b∣分别为
∣
2
∗
a
∣
,
∣
3
∗
a
∣
.
.
.
|2*a|,|3*a|...
∣2∗a∣,∣3∗a∣...,贡献分别为
2
,
3
2,3
2,3… 最后答案
×
4
\times4
×4(正正,正负,负正,负负)
调和级数筛一下,
O
(
n
)
O(n)
O(n)等差数列求也是可以的(懒
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10,mod=1e9+7;
int n;
ll ans;
int main(){
int i,j,k,l,r;
scanf("%d",&n);
for(i=2;i<=n;++i){
for(j=i+i;j<=n;j+=i){
ans+=j/i;
}
}
printf("%I64d\n",ans<<2);
return 0;
}
E.Company
线段树维护区间LCA&dfs序的最大最小值。
每次尝试贪心删去区间dfs序最大/小的点,取最深的LCA即可。
预处理区间LCA复杂度
O
(
n
l
o
g
2
n
)
O(nlog^2n)
O(nlog2n),每次查询最多合并一次
O
(
2
l
o
g
n
)
O(2logn)
O(2logn)
#include<bits/stdc++.h>
#define mid ((l+r)>>1)
#define lc k<<1
#define rc k<<1|1
using namespace std;
const int N=1e5+10;
int n,m,f[N],top[N],dep[N];
int sz[N],son[N],df[N],rv[N],dfn;
int head[N],to[N],nxt[N],tot;
struct node{
int mn,mx,lca;
}t[N<<2],tp;
inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
inline void dfs(int x)
{
sz[x]=1;
for(int j,i=head[x];i;i=nxt[i]){
j=to[i];dep[j]=dep[x]+1;
dfs(j);sz[x]+=sz[j];
if(sz[j]>sz[son[x]]) son[x]=j;
}
}
void dfss(int x,int tpo)
{
top[x]=tpo;df[x]=++dfn;rv[dfn]=x;
if(!son[x]) return;
dfss(son[x],tpo);
for(int j,i=head[x];i;i=nxt[i]){
j=to[i];if(j!=son[x]) dfss(j,j);
}
}
inline int LCA(int x,int y)
{
for(;top[x]!=top[y];x=f[top[x]])
if(dep[top[x]]<dep[top[y]]) swap(x,y);
if(dep[x]<dep[y]) swap(x,y);
return y;
}
inline node mg(node l,node r)
{
node re;
re.mx=max(l.mx,r.mx);
re.mn=min(l.mn,r.mn);
re.lca=LCA(l.lca,r.lca);
return re;
}
void build(int k,int l,int r)
{
if(l==r){t[k].mx=t[k].mn=df[l];t[k].lca=l;return;}
build(lc,l,mid);build(rc,mid+1,r);
t[k]=mg(t[lc],t[rc]);
}
node ask(int k,int l,int r,int L,int R)
{
if(L<=l && r<=R) return t[k];
if(R<=mid) return ask(lc,l,mid,L,R);
if(L>mid) return ask(rc,mid+1,r,L,R);
return mg(ask(lc,l,mid,L,R),ask(rc,mid+1,r,L,R));
}
inline int gt(int l,int r,int pos)
{
if(pos==l) return dep[ask(1,1,n,l+1,r).lca];
if(pos==r) return dep[ask(1,1,n,l,r-1).lca];
return dep[mg(ask(1,1,n,l,pos-1),ask(1,1,n,pos+1,r)).lca];
}
int main(){
int i,j,k,l,r,x,y,ix,iy;
scanf("%d%d",&n,&m);
for(i=2;i<=n;++i) scanf("%d",&f[i]),lk(f[i],i);
dep[1]=1;dfs(1); dfss(1,1);
build(1,1,n);
for(;m;--m){
scanf("%d%d",&l,&r);
tp=ask(1,1,n,l,r);
x=rv[tp.mn];y=rv[tp.mx];
ix=gt(l,r,x);iy=gt(l,r,y);
if(iy>ix) x=y,ix=iy;
printf("%d %d\n",x,ix-1);
}
return 0;
}
F.Upgrading Cities
题解写得很复杂,实际上代码很简单。
先找出 D A G DAG DAG最长的一条链 ( P ) = S 1 , S 2 , . . . , S ∣ P ∣ (P)=S_1,S_2,...,S_{|P|} (P)=S1,S2,...,S∣P∣。
设对于链外的一个点
u
u
u,
S
L
u
S_{L_u}
SLu为标号最大的能到达
u
u
u的点,
S
R
u
S_{R_u}
SRu为标号最小的能被
u
u
u到达的点。
因为图上没有环,所以
L
u
<
R
u
L_u<R_u
Lu<Ru。
可以证明所有 i m p o r t a n t important important的点都在 ( P ) (P) (P)这条链上。
证明:
若存在不在链上的
i
m
p
o
r
t
a
n
t
important
important的点
u
u
u,则必然
L
u
=
R
u
−
1
L_u=R_u-1
Lu=Ru−1,那么
S
L
u
→
u
→
S
L
u
+
1
S_{L_u}\to u \to S_{L_{u+1}}
SLu→u→SLu+1就成了一条增广路,不满足
∣
P
∣
|P|
∣P∣最大的定义。所以
u
u
u必然在链上。
证毕。
所以对于链上的点,可以一遍 t o p o topo topo序 b f s bfs bfs求出每个点能到达的点数 d i d_i di。再建反图 b f s bfs bfs一遍求出每个点能到达的点数 p i p_i pi。判断是否满足 d i + p i ≥ n − 2 d_i+p_i\geq n-2 di+pi≥n−2即可。
考虑如何求不在链上的 s e m i − i m p o r t a n t semi-important semi−important的点,则必然满足 L u + 2 = R u L_u+2=R_u Lu+2=Ru,且没有其它点满足 L v = L u , R v = R u L_v=L_u,R_v=R_u Lv=Lu,Rv=Ru。
而对于代码实现,实际上 b f s bfs bfs的时候只需要考虑弹出队首 x x x后当前队列中剩余的点数 c n t cnt cnt。
- 若 c n t = 0 cnt=0 cnt=0,则 x x x必然在链上,而拓扑序在其后的都是它可以到达的点,总共为 n − r n-r n−r( l l l为队首指针, r r r为队尾指针)。
- 若 c n t > 1 cnt>1 cnt>1,则必然有多个与 x x x同级的链外的点,则 x x x肯定不是 s e m i − i m p o r t a n t / i m p o r t a n t semi-important/important semi−important/important的。
- c n t = 1 cnt=1 cnt=1,需要判断是否 L x + 2 = R x L_x+2=R_x Lx+2=Rx,只需要找与它同级的另一个点的所有可以到达的点中是否有 x x x不能达到的即可。
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int n,m,d[N],p[N],ind[N],Q[N],l,r;
int head[N],to[N],nxt[N],tot,ans;
struct P{int u,v;}le[N];
inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;ind[v]++;}
int main(){
int i,j,x,y,pr;
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i){
scanf("%d%d",&x,&y);lk(x,y);
le[i].u=x;le[i].v=y;
}
l=r=0;
for(i=1;i<=n;++i) if(!ind[i]) Q[r++]=i;
for(;l<r;){
x=Q[l++];
if(l==r) d[x]=n-r;
else if(l+1==r){
y=Q[l];pr=0;
for(i=head[y];i;i=nxt[i]){
j=to[i];if(ind[j]==1) {pr=1;break;}
}
if(pr) d[x]=-n;
else d[x]=n-r;
}else d[x]=-n;
for(i=head[x];i;i=nxt[i]){
j=to[i];ind[j]--;
if(!ind[j]) Q[r++]=j;
}
}
memset(head,0,sizeof(head));
memset(ind,0,sizeof(ind));tot=0;
for(i=1;i<=m;++i) lk(le[i].v,le[i].u);
l=r=0;
for(i=1;i<=n;++i) if(!ind[i]) Q[r++]=i;
for(;l<r;){
x=Q[l++];
if(l==r) p[x]=n-r;
else if(l+1==r){
y=Q[l];pr=0;
for(i=head[y];i;i=nxt[i]){
j=to[i];if(ind[j]==1) {pr=1;break;}
}
if(pr) p[x]=-n;
else p[x]=n-r;
}else p[x]=-n;
for(i=head[x];i;i=nxt[i]){
j=to[i];ind[j]--;
if(!ind[j]) Q[r++]=j;
}
}
for(i=1;i<=n;++i){
if(d[i]+p[i]>=n-2) ans++;
}
printf("%d\n",ans);
return 0;
}