【Codeforces】 Round #520 (Div. 2) A-F

传送门: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=1mpiki
找到最小的 t t t使得 2 t ≥ m a x ( k i ) 2^t\geq max(k_i) 2tmax(ki)
贪心一次性将 n n n转成 ∏ i = 1 m p i 2 t \prod\limits_{i=1}^mp_i^{2^t} i=1mpi2t,最后开 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=rl+1,其中有 x x x个1。
依次取1时构成等比数列: 1 , 2 , 4 , . . . , 2 x − 1 1,2,4,...,2^{x-1} 1,2,4,...,2x1
依次取0时构成等比数列: 2 x − 1 , 2 ( 2 x − 1 ) , 4 ( 2 x − 1 ) . . . 2^x-1,2(2^x-1),4(2^x-1)... 2x1,2(2x1),4(2x1)...
总共为 ( 2 x − 1 ) 2 l e n − x (2^x-1)2^{len-x} (2x1)2lenx,前缀和处理一下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 ∣ &lt; ∣ b ∣ |a|&lt;|b| a<b,对于 ∣ a ∣ |a| a所有可行的 ∣ b ∣ |b| b分别为 ∣ 2 ∗ a ∣ , ∣ 3 ∗ a ∣ . . . |2*a|,|3*a|... 2a,3a...,贡献分别为 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,...,SP

设对于链外的一个点 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 &lt; R u L_u&lt;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=Ru1,那么 S L u → u → S L u + 1 S_{L_u}\to u \to S_{L_{u+1}} SLuuSLu+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+pin2即可。

考虑如何求不在链上的 s e m i − i m p o r t a n t semi-important semiimportant的点,则必然满足 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 nr( l l l为队首指针, r r r为队尾指针)。
  • c n t &gt; 1 cnt&gt;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 semiimportant/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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值