【Codeforces】1142 Round #549 (Div. 1) A-E题解

传送门:CF1142
高质量的Div1


A.The Beatles

枚举起始点相距的整块数 i i i(基础距离为 i ⋅ k i·k ik

2 2 2^2 22枚举起始点分别位于整点前/后 a / b a/b a/b的情况。

n k gcd ⁡ ( n k , l ) \dfrac{nk}{\gcd(nk,l)} gcd(nk,l)nk min ⁡ \min min即可。

复杂度 O ( 4 n ) O(4n) O(4n)

#include
#define fi first
#define sc second 
#define gc getchar
#define pb push_back
using namespace std;
const int N=2e5+10;
typedef long long ll;
typedef double db;

int n,k,a,b;
ll mn=1e18,mx,nw,len;

char cp;
template<class T>inline void rd(T &x)
{
	cp=gc();x=0;int f=0;
	for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
	for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
	if(f) x=-x;
}

int main(){
	int i,x,y;ll ix,iy;
	scanf("%d%d%d%d",&n,&k,&a,&b);
	len=(ll)n*k;
	for(i=0;i<n;++i) 
	  for(x=-1;x<2;x+=2)
	   for(y=-1;y<2;y+=2){
	   	  ix=x*a;iy=(ll)i*k+y*b;
	   	  nw=((iy-ix)%len+len)%len;
	   	  if(nw==0) nw+=len;
	   	  nw=len/__gcd(len,nw);
	   	  mx=max(nw,mx);mn=min(mn,nw);
	   }
	printf("%I64d %I64d",mn,mx);
	return 0;
}

**B.Lynyrd Skynyrd

Atcoder做多了,现在做DS真的菜到不行。

考虑没有 l i , r i l_i,r_i li,ri的限制:
f i f_i fi表示以 a i a_i ai结束的 a a a的子序列满足是 p p p的循环同构序列的最长长度,设 p r e i pre_i prei表示最大的 j j j满足 j < i , p k = a i , p ( k − 1 ) % n = a j j<i,p_k=a_i,p_{(k-1)\%n}=a_j j<i,pk=ai,p(k1)%n=aj,显然 f i = f p r e i + 1 f_i=f_{pre_i}+1 fi=fprei+1

相当于 i → p r e i i\to pre_i iprei连边,构成一个森林。倍增记录点 i i i 2 k 2^k 2k级祖先。

维护 p o s i pos_i posi表示 i i i的第 n − 1 n-1 n1个祖先(即最大的位置 p o s i pos_i posi满足 a a a [ p o s i , i ] [pos_i,i] [posi,i]区间中存在以 i i i结尾的 p p p的循环同构序列)。

( l i , r i ) (l_i,r_i) (li,ri)的询问相当于判断区间内 max ⁡ ( p o s k ) ( k ∈ [ l i , r i ] ) \max(pos_k)(k\in[l_i,r_i]) max(posk)(k[li,ri])是否 ≥ l i \geq l_i li。线段树维护即可。

(我竟然zz的没有想到这样套路的维护,然而动态删区间左端点,没法维护,还上莫队+set了,我怎么这么zz!)

#include
#define fi first
#define sc second 
#define gc getchar
#define pb push_back
#define lc (k<<1)
#define rc (k<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=2e5+10;
typedef long long ll;
typedef double db;

int n,m,q,p[N],a[N],rv[N];
int tg[N],bel[N],pre[N],cnt,v[N],mx[N<<2];
int nt[N][20];
map<int,int>g[N];

char cp;
template<class T>inline void rd(T &x)
{
	cp=gc();x=0;int f=0;
	for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
	for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
	if(f) x=-x;
}

void build(int k,int l,int r)
{
	if(l==r) {mx[k]=v[l];return;}
	build(lc,l,mid);build(rc,mid+1,r);
	mx[k]=max(mx[lc],mx[rc]);
}

int ask(int k,int l,int r,int L,int R)
{
	if(L<=l && r<=R) return mx[k];
	int re=0;
	if(L<=mid) re=ask(lc,l,mid,L,R);
	if(R>mid) re=max(re,ask(rc,mid+1,r,L,R));
	return re;
}

int main(){
	int i,j,k,x,L,R;
	rd(n);rd(m);rd(q);
	for(i=0;i<n;++i) rd(p[i]);
	for(i=1;i<=m;++i) rd(a[i]);
	for(i=0;i<n;++i) rv[p[i]]=p[(i-1+n)%n];
	for(i=1;i<=m;++i){
		pre[a[i]]=i;x=pre[rv[a[i]]];nt[i][0]=x;
		for(j=1;j<20;++j) nt[i][j]=nt[nt[i][j-1]][j-1];
		for(L=n-2,j=0;(1<<j)<=L;++j) if((L>>j)&1) x=nt[x][j];
		v[i]=x;
	}
	build(1,1,m);
	for(;q;--q){
		rd(L);rd(R);
		if(ask(1,1,m,L,R)>=L) putchar('1');
		else putchar('0'); 
	}
	return 0;
}

C.U2

注意函数是 y = x 2 + b x + c y=x^2+bx+c y=x2+bx+c没有 a a a

( x , y ) → ( x , y − x 2 ) (x,y)\to (x,y-x^2) (x,y)(x,yx2),求上凸壳即可。

#include
#define fi first
#define sc second 
#define gc getchar
#define pb push_back
#define lc (k<<1)
#define rc (k<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=1e5+10;
typedef long long ll;
typedef double db;
const db eps=1e-9;

int n,top;
struct P{
	ll x,y;
	bool operator <(const P&ky) const{
	   if(x!=ky.x) return x<ky.x;
	   return y<ky.y;
	}
}p[N],stk[N];

char cp;
template<class T>inline void rd(T &x)
{
	cp=gc();x=0;int f=0;
	for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
	for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
	if(f) x=-x;
}

inline double slope(P a,P b)
{
	if(a.x==b.x) return 1e30;
	return (double)(b.y-a.y)/(double)(b.x-a.x);
 } 

inline void convx()
{
	int i,m=0;
	sort(p+1,p+n+1);
	for(i=1;i<=n;++i)
	   if((i==n && p[i].x!=p[i-1].x) || p[i].x!=p[i+1].x) p[m++]=p[i];
	stk[(top=1)]=p[0];
	for(i=1;i<m;++i){
		for(;top>1 && slope(stk[top],p[i])+eps>=slope(stk[top-1],stk[top]);--top);
		stk[++top]=p[i];
	}
}

int main(){
	int i,j,k,x,y;
	rd(n);
	for(i=1;i<=n;++i){
		rd(x);rd(y);
		p[i].y=y-(ll)x*x;p[i].x=x;
	}
	convx();
	printf("%d",max(0,top-1));
	return 0;
}

D.Foreigner

介绍一种 O ( N ⋅ 11 ) O(N·11) O(N11)的方法:

归纳求出合法数 x x x的排名 r k x rk_x rkx(设 x x x的长度为 l e n len len):

  • 注意到 0 + 1 + . . . + 10 = 55 , 11 ∣ 55 0+1+...+10=55,11|55 0+1+...+10=55,1155
  • l e n = 1 len=1 len=1 1 ≤ x ≤ 9 1\leq x\leq 9 1x9均合法
  • l e n > 1 len>1 len>1,假设 q = r k ⌊ x 10 ⌋ q=rk_{\lfloor \frac x{10}\rfloor} q=rk10x。考虑把 1 − ⌊ x 10 ⌋ 1-\lfloor \frac x{10}\rfloor 110x中所有合法数都拓展 1 1 1位再加上 1 − 9 1-9 19
    11 ⌊ q 11 ⌋ 11\lfloor \frac q{11}\rfloor 1111q个合法数可以分成 ⌊ q 11 ⌋ \lfloor \frac q{11}\rfloor 11q个排名相邻的大小为 11 11 11的组,每组拓展后都得到 55 55 55个数,在 ( m o d 11 ) \pmod{11} (mod11)意义下无贡献。
    考虑 q ′ = q % 11 q'=q\% 11 q=q%11的部分,前 ( q − 1 ) (q-1) (q1)个数总共拓展出 x ( x − 1 ) 2 \dfrac{x(x-1)}{2} 2x(x1)个合法数,再加上第 q q q个( ⌊ x 10 ⌋ \lfloor \frac x{10}\rfloor 10x)拓展出的 x % 10 + 1 x\%10+1 x%10+1个。
    所以只需要 r k ⌊ x 10 ⌋ ′ = r k ⌊ x 10 ⌋ % 11 rk'_{\lfloor \frac x{10}\rfloor}=rk_{\lfloor \frac x{10}\rfloor}\% 11 rk10x=rk10x%11的值,就可以推出 r k x ′ = r k ⌊ x 10 ⌋ ′ ( r k ⌊ x 10 ⌋ ′ − 1 ) 2 + x % 10 + 10 rk'_x=\dfrac{rk'_{\lfloor \frac x{10}\rfloor}(rk'_{\lfloor \frac x{10}\rfloor}-1)}{2}+x\%10+10 rkx=2rk10x(rk10x1)+x%10+10

d p [ i ] [ j ] dp[i][j] dp[i][j]表示处理到第 i i i位,前面可行解的排名 m o d   11 mod\ 11 mod 11的值为 j j j时最远能拓展到的位置。

详见代码

#include
using namespace std;
const int N=1e5+10;
typedef long long ll;

int n,f[N][12];
char s[N];ll ans; 

int dp(int pos,int c)
{
	if(~f[pos][c]) return f[pos][c];
	if(pos>n) {f[pos][c]=n;return n;}
	int nw=s[pos];
	if(nw>=c) {f[pos][c]=pos-1;return pos-1;}
	f[pos][c]=dp(pos+1,((c*(c-1)>>1)+nw+10)%11);
	return f[pos][c];
}

int main(){
	int i,j;
	memset(f,0xff,sizeof(f));
	scanf("%s",s+1);n=strlen(s+1);
	for(i=1;i<=n;++i) s[i]-='0'; 
	for(i=1;i<=n;++i) if(s[i])
	  ans+=dp(i+1,s[i])-i+1;
	cout<<ans;
	return 0;
} 

E.Pink Floyd

考虑没有粉边限制时:

任意选一个点作为初始点 t o p top top,并记录它通过绿边可以到达的点集 S S S
每次取任意一个它不能到达的点 y y y,判断 ( t o p , y ) (top,y) (top,y)连边方向:若是 t o p top top指向 y y y,将 y y y加入 S S S即可;否则将 y y y作为 t o p top top,并将之前的 t o p top top加入 S S S

显然 n n n次操作后 S = ∣ n − 1 ∣ S=|n-1| S=n1

考虑存在粉边限制时:

( t o p , y ) (top,y) (top,y)之间的连边可能是粉色的,不能保证路径上边颜色相同。

题解提供了一种构造方法:

将原图(只存在粉边)缩点,任选一个入度为 0 0 0的SCC作为 t o p top top,并任选其中一个点作为 t p tp tp

如果只有这一个入度为 0 0 0的SCC,答案就是 t p tp tp

否则任选另一个入度为0的SCC中的某个点 y y y,判断 ( t p , y ) (tp,y) (tp,y)连边方向。
假设 t p tp tp指向 y y y(否则 t o p top top转成这个SCC),相当于删去了 y y y这个点——如果 y y y所属SCC中所有点都被删完了,相当于删去了这个 S C C SCC SCC,同时删去它的出边,新生成一些入度为0的SCC。

显然最多 n − 1 n-1 n1次后得到答案 t p tp tp

p.s
在删SCC点时,不需要考虑删点后SCC不强连通的情况(不然写起来很麻烦),只需要处理SCC点被删完的情况——相当于求出一条 t p tp tp出发的能走完一个SCC的绿边路径。
复杂度 O ( n + m ) O(n+m) O(n+m)

代码

#include
#define pb push_back
using namespace std;
const int N=1e5+10;
typedef long long ll;

int n,m,num,ind[N],bel[N];
map<int,int>mp[N];
vector<int>g[N],nt[N],st[N];
int q[N],tl;

int df[N],low[N],dfn,stk[N],top;

void tar(int x)
{
	df[x]=low[x]=++dfn;stk[++top]=x;
	for(int y:g[x]){
		if(!df[y]){
			tar(y);low[x]=min(low[x],low[y]);
		}else if(!bel[y]) low[x]=min(low[x],df[y]);
	}
	if(low[x]==df[x]){
		num++;
		for(int tp;;){
			tp=stk[top--];bel[tp]=num;
			st[num].pb(tp);if(tp==x) break;
		}
	}
}

inline int ask(int x,int y)
{
	cout<<"? "<<x<<" "<<y<<endl;
	int re;cin>>re;
	return re;
}

int main(){
	int i,x,y,b;
	cin>>n>>m;
	for(i=1;i<=m;++i){
		cin>>x>>y;g[x].pb(y);
	}
	for(i=1;i<=n;++i) if(!df[i]) tar(i);
	for(i=1;i<=n;++i){
		b=bel[i];
		for(int y:g[i]) if(bel[y]!=b && (!mp[b][bel[y]])){
			nt[b].pb(bel[y]);ind[bel[y]]++;mp[b][bel[y]]=1;
		}
	}
	for(i=1;i<=num;++i) if(!ind[i]) q[++tl]=i;
	for(;tl>1;){
		x=st[q[1]].back();y=st[q[2]].back();
		if(!ask(x,y)) swap(q[2],q[1]);
		y=q[2];st[y].pop_back();
		if(!st[y].size()){
			swap(q[2],q[tl]);--tl;
			for(int b:nt[y]){
					ind[b]--;if(!ind[b]) q[++tl]=b;
			}
		}
	} 
	cout<<"! "<<st[q[1]].back()<<endl;
	return 0;
} 

你可能感兴趣的:(线段树,Tarjan缩点,凸包,构造)