Educational Codeforces Round 92 (Rated for Div. 2) 题解 (A到G)

目录

  • CF1389A - LCM Problem
  • CF1389B - Array Walk
  • CF1389C - Good String
  • CF1389D - Segment Intersections
  • CF1389E - Calendar Ambiguity
  • CF1389F - Bicolored Segments
  • CF1389G - Directing Edges

CF1389A - LCM Problem

z = L C A ( x , y ) , l ≤ x , y , z ≤ r z=LCA(x,y),l\leq x,y,z\leq r z=LCA(x,y),lx,y,zr,当 y = z y=z y=z时, L C A ( x , y ) = z LCA(x,y)=z LCA(x,y)=z,所以这时候 x ∗ k = y x*k=y xk=y,应为 x ≠ y x\neq y x=y所以 k ≥ 2 k\geq2 k2, x x x最小取 l l l,y最小取 2 ∗ l 2*l 2l
所以只要检验 2 ∗ l 2*l 2l是否 ≤ r \leq r r就行了。

CF1389B - Array Walk

很明显因为不可以连续向左走,所以只可能是这样的走法:
线一直往前走,回头走一步,再往前,回头走一步……
假设再 i i i的位置回头,则回头一布再往前一步就相当于多得了 a i + a i − 1 a_i+a_{i-1} ai+ai1分,贪心考虑一定是再最大的 a i + a i − 1 a_i+a_{i-1} ai+ai1地方一直回头再往前,所以只需要枚举走到哪里,前面最大的 a i + a i − 1 a_i+a_{i-1} ai+ai1是多少就行了。
Code:(珍爱生命远离抄袭)

#include
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define int LL
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
int n;
int a[100000+20];
signed main(){
	fastio;
	int t;
	cin>>t;
	while(t--){cin>>n;
		int k,z;
		cin>>k>>z;
	rb(i,1,n) cin>>a[i];
	LL res=a[1],sum=a[1],maxi=0;
	rb(i,2,n){
		sum+=a[i];
		maxi=max((LL)a[i]+a[i-1],maxi);
		LL ok=k-i+1;
		if(ok<0) break;
		if(ok>0) 
		ok=min((ok)/2,(LL)z);
		else ok=0;
		res=max(res,sum);
		res=max(res,sum+ok*maxi);
		LL tmp=sum,tmp2=0;
		int rest=k-i+1; 
		rb(j,1,1000)
		{
			if(rest==0) break;
			rest--;
			if(j%2==1){
				if(tmp2==z) break;
				tmp2++;
				tmp+=a[i-1];
			}
			else{
				tmp+=a[i];
			}
			if(tmp2==z) break;
			if(rest==0) break;
		}
//		cout<
		res=max(res,tmp);
	}
	cout<<res<<endl;
	}
	return 0;
}

CF1389C - Good String

两次旋转转 t n 与 t 2 , t 3 与 t 1 , t 2 与 t 5 t_n与t_2,t_3与t_1,t_2与t_5 tnt2,t3t1,t2t5一 一对应。
可以发现最终的串有两种符合条件的情况:

  1. 所有位上的字符都相同
  2. 长度是偶数,奇数位的都相同,偶数位的都相同,如:010101,两次旋转都是101010

对于第一种,枚举剩下的是什么字符就行了,对于第二宗枚举奇数位的和偶数位的字符。
code:

/*
AuThOr Gwj
*/
#include
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
int t;
int dp[2];
int main(){
	fastio;
	cin>>t;
	while(t--){
		string s;
		cin>>s;
		int n=s.length();
		int best=0;
		
		rb(i,0,9)
			rb(j,0,9){
				dp[0]=dp[1]=-INF;
				dp[1]=0;
				rep(k,n){
					int l0,l1;
					l0=dp[0];
					l1=dp[1];
					if(s[k]=='0'+i){
						dp[0]=max(dp[0],l1+1);
					}
					if(s[k]=='0'+j){
						dp[1]=max(dp[1],l0+1);
					}
				}
				best=max(best,dp[1]);
			}
		int res=n-best;
		int cnt[10]={0};
		rep(i,n)
			cnt[s[i]-'0']++;
		rb(i,0,9)
			res=min(res,n-cnt[i]);
		cout<<res<<endl;
	}	
	return 0;
}

CF1389D - Segment Intersections

首先如果初始状态不相交,则必须要使至少一对达到相交状态。使一对相交后,必然存在一段时间只要扩展任意一个一次,答案+1。以后必须要两个都扩展才可以加1。所以只需要枚举使多少对相交就行了。
code:

/*
AuThOr Gwj
*/
#include
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define int LL
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
int n,k,l1,r1,l2,r2;
int check(int L1,int R1,int L2,int R2){
	if(R1<L2){
		return 0;
	}
	return min(R1,R2)-max(L1,L2);
}
void solve(){
	R2(n,k);
	R2(l1,r1);
	R2(l2,r2);
	if(l1>l2){
		swap(l1,l2);
		swap(r1,r2);
	}
	int reach=0;
	if(r1<l2){
		reach=l2-r1;
	}
	LL sa;
	LL init=sa=check(l1,r1,l2,r2);
	init*=n;
	k-=init;
	if(k<=0){
		cout<<0<<endl;
		return ;
	}
	int maxi=max(r1,r2)-min(l1,l2)-sa;
	LL res=0;
	LL tmp=1000000000000000;
	rb(i,1,n){
		res+=reach;
		if(maxi>=k){
			res+=k;
			k=0;
			break;
		}
	else{
		res+=maxi;
		k-=maxi;
		tmp=min(tmp,res+2*k);
	}
	}
	if(k>0){
		cout<<tmp<<endl;
	}
	else
	cout<<min(res,tmp)<<endl; 
}
signed main(){
	fastio;
	int t;
	cin>>t;
	while(t--) {solve();	}
	return 0;
}

CF1389E - Calendar Ambiguity

根据题意可列出方程:
m x + y ≡ m y + x ( m o d w , 1 ≤ y , x ≤ m i n ( m , d ) ) m x − x ≡ m y − y ( m o d w , 1 ≤ y , x ≤ m i n ( m , d ) ) x ∗ ( m − 1 ) ≡ y ∗ ( m − 1 ) ( m o d w , 1 ≤ y , x ≤ m i n ( m , d ) ) ( y − x ) ∗ ( m − 1 ) ≡ 0 ( m o d W ) mx+y\equiv my+x(mod w,1\leq y,x\leq min(m,d))\\ mx-x\equiv my-y(mod w,1\leq y,x\leq min(m,d))\\ x*(m-1)\equiv y*(m-1) (mod w,1\leq y,x\leq min(m,d))\\ (y-x)*(m-1) \equiv 0 (mod W) mx+ymy+x(modw,1y,xmin(m,d))mxxmyy(modw,1y,xmin(m,d))x(m1)y(m1)(modw,1y,xmin(m,d))(yx)(m1)0(modW)
z = l c m ( ( m − 1 ) % w , w ) / ( m − 1 ) % w z=lcm((m-1)\%w,w)/(m-1)\%w z=lcm((m1)%w,w)/(m1)%w,所以 ( y − x ) ≡ z ( m o d W ) (y-x)\equiv z(modW) (yx)z(modW)
然后就可以算了。
code:

/*
AuThOr Gwj
*/
#include
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define int LL
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
int t;
signed main(){
	fastio;
	cin>>t;
	while(t--){
		int m,d,w;
		R2(m,d);
		R(w);
		if(d==1){
			cout<<0<<endl;
			continue;
		}
		int z=d-1;
		z%=w;
		if(z==0){
			cout<<(LL)min(d,m)*(min(d,m)-1)/2<<endl;
			continue;
		}
		int best=z*w/__gcd(z,w)/z;
		if(best>=d||best>=m){
			cout<<0<<endl;
			continue;
		}
		int r=min(d,m);
		LL res=0;
		int fr=r%best;
		int can=r/best;
			if(fr)
			res+=fr*can;
			can--;
			if(can>0)
			res+=(can)*(can+1)/2*best;
		cout<<res<<endl;
	}
	return 0;
}

CF1389F - Bicolored Segments

考虑最后剩下的是什么样的情况:
大概是这样:
一段区间内只有颜色为1的线段,一段区间内只有颜色为2的线段,一段区间内只有颜色为1的线段……
这样就可以想到dp。*下文方便起见,颜色变为1,0
d p i , j − > 从 i 开 始 了 一 段 只 有 颜 色 j 的 区 间 dp_{i,j}->从i开始了一段只有颜色j的区间 dpi,j>ij
d p i , j = m a x { d p k , j X o r 1 + f ( i , k − 1 , j ) } dp_{i,j}=max\{dp_{k,{j Xor 1}}+f(i,k-1,j)\} dpi,j=max{dpk,jXor1+f(i,k1,j)}
f ( l , r , j ) − > 有 多 少 颜 色 为 j 的 线 段 再 区 间 [ l , r ] 内 f(l,r,j)->有多少颜色为j的线段再区间[l,r]内 f(l,r,j)>j线[l,r]
这样的转移时 O ( m a x { r i } 2 ) O(max\{r_i\}^2) O(max{ri}2),直接爆炸。
接下来想怎样优化。
很明显可以离散化,复杂度变为了 O ( n 2 ) O(n^2) O(n2),呃(⊙﹏⊙),还是过不了。
接下来我们考虑 d p i , j 与 d p i + 1 , j dp_{i,j}与dp_{i+1,j} dpi,jdpi+1,j的转移有什么区别。对!考虑了左端点为i的颜色为j的线段的 贡献。可以用线段树来优化!
code:

/*
AuThOr Gwj
*/
#include
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
int n;
const int MAXN=2e5;
int delta[MAXN*8][2],tree[MAXN*8][2];
vector<pair<int,bool > > rsave[MAXN*2];
const int N=1<<19;
int add(int a,int b,bool flag,int now=1,int l=1,int r=N+1){
	if(r<=a||l>=b){
		return tree[now][flag]+delta[now][flag];
	}
	if(r<=b&&l>=a){
		delta[now][flag]++;
//		cout<
		return tree[now][flag]+delta[now][flag];
	}
	int mid=(l+r)>>1;
	return (tree[now][flag]=max(add(a,b,flag,now<<1,l,mid),add(a,b,flag,now<<1|1,mid,r)))+delta[now][flag];
}
int query(int a,int b,bool flag,int now=1,int l=1,int r=N+1){
//		cout<
	if(r<=a||l>=b) return 0;
	if(r<=b&&l>=a){
		return tree[now][flag]+delta[now][flag];
	}
	int mid=(l+r)>>1;
//	cout<
	return max(query(a,b,flag,now<<1,l,mid),query(a,b,flag,now<<1|1,mid,r))+delta[now][flag];
}
void push_down(int index,bool flag){
	delta[index<<1][flag]+=delta[index][flag];
	delta[index<<1|1][flag]+=delta[index][flag];
	tree[index][flag]+=delta[index][flag];
	delta[index][flag]=0;
}
void go(int a,bool flag,int now=1,int l=1,int r=N+1){
	if(r<=a||l>a){
		return;
	}
	if(l==r-1){
		tree[now][flag]+=delta[now][flag];
		delta[now][flag]=0;
		return;	
	} 
	push_down(now,flag);
	int mid=(l+r)>>1;
	go(a,flag,now<<1,l,mid);
	go(a,flag,now<<1|1,mid,r);
}
void change(int index,bool flag,int val){
	index+=N-1;
	tree[index][flag]=val;
	index>>=1;
	while(index){
		tree[index][flag]=max(
		tree[index<<1][flag]+delta[index<<1][flag],
		tree[index<<1|1][flag]+delta[index<<1|1][flag]
		);
		index>>=1;
	}
}
vector<mp> seg[2];
map<int,int> m;
int main(){
	fastio;
	R(n);
	rb(i,1,n){
		int l,R;
		R2(l,R);
		int co;
		R(co);
		co--;
		seg[co].PB(II(l,R)); 
		m[l]=m[R]=1;
	}
	int cnt=0;
	for(map<int,int> :: IT ite=m.begin();ite!=m.end();ite++){
		ite->SEC=++cnt; 
	}
	for(auto it:seg[0]){
		it.FIR=m[it.FIR];
		it.SEC=m[it.SEC];
//		cout<
		rsave[it.FIR].PB(II(it.SEC,0));
	}
	for(auto it:seg[1]){
		it.FIR=m[it.FIR];
		it.SEC=m[it.SEC];
//		cout<
		rsave[it.FIR].PB(II(it.SEC,1));
	}
	rl(i,cnt,1){
		for(auto it:rsave[i]){
			if(it.SEC==1){
				add(it.FIR+1,N+1,0);
			}
			else{
				add(it.FIR+1,N+1,1);
			}
		}
		go(i,0);
		go(i,1); 
		change(i,0,query(i+1,N+1,1));
		change(i,1,query(i+1,N+1,0));
	}
	cout<<max(query(1,cnt+1,0),query(1,cnt+1,1))<<endl;
	return 0;
}

CF1389G - Directing Edges

首先双联通分量缩点。因为双联通分量存在一种定向方式使得每一对点可以互相到达。可以参考这题。
然后缩完点的图是一棵树,可以dp来做。
d p [ i ] 表 示 i 是 s a t u r a t e d , 以 i 为 根 的 子 树 最 大 值 为 多 少 dp[i]表示i是saturated ,以i为根的子树最大值为多少 dp[i]isaturatedi
换根dp就ok了。

/*
AuThOr Gwj
*/
#include
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define int LL
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
const int MAXN=3e5+2;
int n,m,k,c[MAXN],w[MAXN],cBcc[MAXN],depth[MAXN],low[MAXN],belong[MAXN],cntBcc;
bool sp[MAXN],spBcc[MAXN],vis[MAXN];
vector<mp> g[MAXN];
stack<int> sta;
void dfs(int now,int pre,int deep){
	sta.push(now);
	depth[now]=deep;
	low[now]=deep;
	vis[now]=1;
	for(auto it:g[now]){
		int to=it.FIR;
		if(to!=pre){
			if(vis[to]){
				low[now]=min(low[now],depth[to]);
			}
			else{
				dfs(to,now,deep+1);
				low[now]=min(low[now],low[to]);
			}
		}
	}
	if(low[now]==deep){
		cntBcc++;
		while(1){
			int index=sta.top();
			sta.pop();
			belong[index]=cntBcc; 
			if(index==now) break;
		} 
	}
}
int cntSp[MAXN];
int cntHave[MAXN];
int total=0;
LL dp[MAXN];//以i为根的子树含有special node数量 |..#..| i号点为saturated的最优代价 
LL totC[MAXN],sumC[MAXN];//Σc[j](j∈son(i),cntSp[j]=0)+cBcc[i]
void dfsInit(int now,int pre){
	cntSp[now]=spBcc[now];
	cntHave[now]=spBcc[now];
	total+=spBcc[now];
	sumC[now]=cBcc[now];
	for(auto it:g[now])
	{
		if(it.FIR!=pre)
		{
			dfsInit(it.FIR,now);
			sumC[now]+=sumC[it.FIR];
			cntSp[now]+=cntSp[it.FIR];
			if(cntSp[it.FIR]){
				cntHave[now]++;
			}
		}
	}
} 
int onlySon[MAXN][2];
void dfsDp(int now,int pre){
	onlySon[now][0]=onlySon[now][1]=-1; 
	totC[now]=cBcc[now];
	for(auto it:g[now]){
		if(it.FIR!=pre)
		if(!cntSp[it.FIR]){
			totC[now]+=sumC[it.FIR];
		}
	}
	dp[now]=totC[now];
	for(auto it:g[now]){
		if(it.FIR!=pre)
		if(cntSp[it.FIR]){
			if(onlySon[now][0]==-1){
				onlySon[now][0]=it.FIR;
			}
			else{
				onlySon[now][1]=it.FIR;
			}
			dfsDp(it.FIR,now);
			if(cntSp[it.FIR]==total){
				dp[now]+=dp[it.FIR];
			}
			else
			dp[now]+=max(0ll,dp[it.FIR]-it.SEC);
		}
	}
}
int res[MAXN];
struct DATA{
	int cntSp,dp,totC,sumC;
};
map<mp,int> id;
pair<DATA,DATA> Copy(int i,int j){
	DATA d1,d2;
	d1.cntSp=cntSp[i];
	d1.dp=dp[i];
	d1.sumC=sumC[i];
	d1.totC=totC[i];
	d2.cntSp=cntSp[j];
	d2.dp=dp[j];
	d2.sumC=sumC[j];
	d2.totC=totC[j]; 
	return II(d1,d2);
}
void  Paste(int i,int j,DATA d1,DATA d2){
	cntSp[i]=d1.cntSp;
	dp[i]=d1.dp;
	sumC[i]=d1.sumC;
	totC[i]=d1.totC;
	cntSp[j]=d2.cntSp;
	dp[j]=d2.dp;
	sumC[j]=d2.sumC;
	totC[j]=d2.totC; 
}
void change_root(int now,int pre){
//	cout<
	res[now]=dp[now];
	for(auto it2:g[now]){
		int it=it2.FIR;
		if(it!=pre){
			mp saveSon=II(onlySon[now][0],onlySon[now][1]);
			int saveHave1=cntHave[now],saveHave2=cntHave[it];
			pair<DATA,DATA> save;
			save=Copy(now,it);
			if(!cntSp[it]){
				totC[now]-=sumC[it];
			}
			if(cntSp[it]){
				if(cntSp[it]!=total)
				dp[now]-=max(0ll,dp[it]-it2.SEC);
				else 
				dp[now]-=dp[it];
			}
			dp[now]-=totC[now];
			dp[it]-=totC[it];
			sumC[now]-=sumC[it];
			sumC[it]+=sumC[now];
			cntSp[now]-=cntSp[it];
			cntSp[it]+=cntSp[now];
			cntHave[it]+=(bool)(cntSp[now]);
			if(!cntSp[now]){
				totC[it]+=sumC[now];
			}
			dp[now]+=totC[now];
			dp[it]+=totC[it];
			if(cntSp[now]){
				if(cntSp[now]!=total){
					if(dp[now]-it2.SEC>0){
						dp[it]+=dp[now]-it2.SEC;
					}
				}
				else{
					dp[it]+=dp[now];
				}
			}
			change_root(it,now);
			Paste(now,it,save.FIR,save.SEC);
			cntHave[now]=saveHave1,cntHave[it]=saveHave2;
			onlySon[now][0]=saveSon.FIR;
			onlySon[now][1]=saveSon.SEC;
		}
	}
}
signed main(){
	fastio;
	cin>>n>>m>>k;
	rb(i,1,k){
		int vi;
		cin>>vi;
		sp[vi]=1;
	}
	rb(i,1,n) R(c[i]);
	rb(i,1,m){
		int wi;
		R(wi);
		w[i]=wi;
	}
	vector<mp> edges;
	rb(i,1,m){
		int u,v;
		R2(u,v);
		id[II(u,v)]=id[II(v,u)]=i;
		edges.PB(II(u,v));
		g[u].PB(II(v,w[i]));
		g[v].PB(II(u,w[i]));
	}
	dfs(1,0,1);//
//	cout<
//	rb(i,1,n) cout<
//	rb(i,1,n)
//		belong[i]=i;
	rb(i,1,n)
		cBcc[belong[i]]+=c[i];
	rb(i,1,n)
		spBcc[belong[i]]|=sp[i];
	rb(i,1,n)
		g[i].clear();
	rep(i,m){
		int u,v;
		u=edges[i].FIR;
		v=edges[i].SEC;
		v=belong[v];
		u=belong[u];
		if(u!=v){
			g[u].PB(II(v,w[i+1]));
			g[v].PB(II(u,w[i+1]));
		}
	}
	dfsInit(1,0);
	dfsDp(1,0);
	change_root(1,0);
	rb(i,1,n){
		cout<<res[belong[i]]<<" ";
	}cout<<endl;
	return 0;
}

你可能感兴趣的:(题解)