20240724模拟赛订正题笔记

(T1)lnsyoj2208 逆流而上/P10737 [SEERC2020] Reverse Game

考虑到失败时字符串应为前面都是0,后面都是1(例如"0000001111111")
所以可以将原串的逆序对数求出,记为m,对于每个可翻转的串进行分类讨论:
1."10"->"01"可以将原串的逆序对减1。
2."100"->"001" "110"->"011" "1010"->"0101"可以将原串的逆序对减2。
综上所述,该问题变成了取石子问题。
所以当m%3==0时后手(B)胜,否则先手(A)胜。
代码如下:

#include <cstdio>
#include <cstring>
#define int long long
using namespace std;
int cnt1;
int T;
int n;
char s[1000005];
signed main(){
	scanf("%lld",&T);
	while(T--){
		scanf("%s",s+1);
		n=strlen(s+1);
		cnt1=0;
		int res=0;
		for(int i=1;i<=n;i++){
			if(s[i]=='1'){
				cnt1++;
			}
			else if(s[i]=='0'){
				res+=cnt1;
			}
		}
		if(res%3==0){
			printf("B\n");
		}
		else{
			printf("A\n");
		}
	}
	return 0;
}

(T2)lnsyoj2209 帝国飘摇/P10455 Genius Acm

首先若固定一组零件的左端点,则右端点越靠右越好,所以选择贪心。
对于一组零件内,可以一个指针从大到小选数,另一个指针从小到大选数,这样即可以使val最大。
所以可以倍增枚举右端点,枚举时进行排序,时间复杂度\(O(nlog^{2}n)\),可拿90pts。
排序时可以不排整个一组,可以只排新增加进的零件,然后原先一组和新进的零件都是有序的,所以可以\(O(n)\)合并,所以时间复杂度优化成了\(O(nlogn)\),可拿100pts。
代码如下:

#include <cstdio>
#include <algorithm>
#define int long long
using namespace std;
int T;
int n,m,k;
struct vi{
	int data;
}vis[500005],gbvis[500005],yvis[500005];
bool cmp1(vi a,vi b){
	return a.data<b.data;
}
signed main(){
	scanf("%lld",&T);
	while(T--){
		scanf("%lld%lld%lld",&n,&m,&k);
		for(int i=1;i<=n;i++){
			scanf("%lld",&yvis[i].data);
		}
		int lr=0;
		int ans=0;
		for(int l=1,r=0,base=0;l<=n;base++){
			if(base==-1){
				l=r+1;
				base=0;
				ans++;
			}
			lr=r;
			r+=1<<base;
			if(r>n){
				base-=2;
				r=lr;
			}
			for(int i=lr+1;i<=r;i++){
				vis[i].data=yvis[i].data;
			}
			sort(vis+lr+1,vis+r+1,cmp1);
			if(l<=lr){
				int i1=l,j1=lr+1,k1=l;
				while(i1<=lr && j1<=r){
					if(vis[i1].data<vis[j1].data){
						gbvis[k1].data=vis[i1].data;
						k1++;
						i1++;
					}
					else{
						gbvis[k1].data=vis[j1].data;
						k1++;
						j1++;
					}
				}
				while(i1<=lr){
					gbvis[k1].data=vis[i1].data;
					k1++;
					i1++;
				}
				while(j1<=r){
					gbvis[k1].data=vis[j1].data;
					k1++;
					j1++;
				}
			}
			else{
				for(int i=l;i<=r;i++){
					gbvis[i].data=vis[i].data;
				}
			}
			int x=r-l+1;
			int val=0;
			if(x<2*m){
				for(int i=1;i<=x/2;i++){
					val+=(gbvis[l+i-1].data-gbvis[r-i+1].data)*(gbvis[l+i-1].data-gbvis[r-i+1].data);
				}
			}
			else{
				for(int i=1;i<=m;i++){
					val+=(gbvis[l+i-1].data-gbvis[r-i+1].data)*(gbvis[l+i-1].data-gbvis[r-i+1].data);
				}
			}
			if(val>k){
				base-=2;
				r=lr;
			}
			else{
				for(int i=l;i<=r;i++){
					vis[i].data=gbvis[i].data;
				}
			}
		}
		printf("%lld\n",ans); 
	}
	return 0;
}

(T3)lnsyoj2210 致命冲击/P5069 [Ynoi2015] 纵使日薄西山

此题我们可以利用线段树维护此序列的权值,具体如下:
在一个操作中,若选择了一个数,则这个数两边的数不能被选。(证明:因为若选择此数,则必须满足此数大于这个数两边的数,通过不等式的性质得知这个数两边的数在之后的操作中也不能被选)
可已将此序列拆成若干序列,通过上个结论可以使线段树具有可合并性,所以对这些数列进行合并,合并过程充分利用了pushup。
两个序列合并一共分为两种情况:
1.左序列最右的数被选 且 右序列最左的数被选:
此时选两个数中最大的数,另一个数强制不被选。
2.左序列最右的数没被选 或 右序列最左的数没被选 或 两个数都没被选:
此时直接合并即可。
所以线段树需要维护:(左端点:序列最左的元素 右端点:左端点:序列最右的元素)
1.当序列左右端点都可以被选时序列的权值
2.当序列左端点强制不被选 且 右端点可以被选 时序列的权值
3.当序列右端点强制不被选 且 左端点可以被选 时序列的权值
4.当序列左端点强制不被选 且 右端点强制不被选 时序列的权值
5.当序列左右端点都可以被选时序列左端点是否被选中
6.当序列左端点强制不被选 且 右端点可以被选 时序列左端点是否被选中
7.当序列右端点强制不被选 且 左端点可以被选 时序列左端点是否被选中
8.当序列左端点强制不被选 且 右端点强制不被选 时序列左端点是否被选中
9.当序列左右端点都可以被选时序列右端点是否被选中
10.当序列左端点强制不被选 且 右端点可以被选 时序列右端点是否被选中
11.当序列右端点强制不被选 且 左端点可以被选 时序列右端点是否被选中
12.当序列左端点强制不被选 且 右端点强制不被选 时序列右端点是否被选中
13.序列左端点的大小
14.序列右端点的大小
pushup函数需要对以上所有进行维护
代码如下:

#include <cstdio>
#define int long long
using namespace std;
struct node{
	int data[4];//0:YY 1:YN 2:NY 3:NN
	bool xl[4];
	bool xr[4];
	int shul;
	int shur;
}ns[400005];
int n,q,a[100005];
inline void pushup(int id){
	if(ns[id*2].xr[0] && ns[id*2+1].xl[0]){
		if(ns[id*2].shur>=ns[id*2+1].shul){
			ns[id].xl[0]=ns[id*2].xl[0];
			ns[id].xr[0]=ns[id*2+1].xr[2];
			ns[id].data[0]=ns[id*2].data[0]+ns[id*2+1].data[2];
		}
		else{
			ns[id].xl[0]=ns[id*2].xl[1];
			ns[id].xr[0]=ns[id*2+1].xr[0];
			ns[id].data[0]=ns[id*2].data[1]+ns[id*2+1].data[0];
		}
	}
	else{
		ns[id].xl[0]=ns[id*2].xl[0];
		ns[id].xr[0]=ns[id*2+1].xr[0];
		ns[id].data[0]=ns[id*2].data[0]+ns[id*2+1].data[0]; 
	}
	if(ns[id*2].xr[0] && ns[id*2+1].xl[1]){
		if(ns[id*2].shur>=ns[id*2+1].shul){
			ns[id].xl[1]=ns[id*2].xl[0];
			ns[id].xr[1]=ns[id*2+1].xr[3];
			ns[id].data[1]=ns[id*2].data[0]+ns[id*2+1].data[3];
		}
		else{
			ns[id].xl[1]=ns[id*2].xl[1];
			ns[id].xr[1]=ns[id*2+1].xr[1];
			ns[id].data[1]=ns[id*2].data[1]+ns[id*2+1].data[1];
		}
	}
	else{
		ns[id].xl[1]=ns[id*2].xl[0];
		ns[id].xr[1]=ns[id*2+1].xr[1];
		ns[id].data[1]=ns[id*2].data[0]+ns[id*2+1].data[1];
	}
	if(ns[id*2].xr[2] && ns[id*2+1].xl[0]){
		if(ns[id*2].shur>=ns[id*2+1].shul){
			ns[id].xl[2]=ns[id*2].xl[2];
			ns[id].xr[2]=ns[id*2+1].xr[2];
			ns[id].data[2]=ns[id*2].data[2]+ns[id*2+1].data[2];
		}
		else{
			ns[id].xl[2]=ns[id*2].xl[3];
			ns[id].xr[2]=ns[id*2+1].xr[0];
			ns[id].data[2]=ns[id*2].data[3]+ns[id*2+1].data[0];
		}
	}
	else{
		ns[id].xl[2]=ns[id*2].xl[2];
		ns[id].xr[2]=ns[id*2+1].xr[0];
		ns[id].data[2]=ns[id*2].data[2]+ns[id*2+1].data[0];
	}
	if(ns[id*2].xr[2] && ns[id*2+1].xl[1]){
		if(ns[id*2].shur>=ns[id*2+1].shul){
			ns[id].xl[3]=ns[id*2].xl[2];
			ns[id].xr[3]=ns[id*2+1].xr[3];
			ns[id].data[3]=ns[id*2].data[2]+ns[id*2+1].data[3];
		}
		else{
			ns[id].xl[3]=ns[id*2].xl[3];
			ns[id].xr[3]=ns[id*2+1].xr[1];
			ns[id].data[3]=ns[id*2].data[3]+ns[id*2+1].data[1];
		}
	}
	else{
		ns[id].xl[3]=ns[id*2].xl[2];
		ns[id].xr[3]=ns[id*2+1].xr[1];
		ns[id].data[3]=ns[id*2].data[2]+ns[id*2+1].data[1];
	}
	ns[id].shul=ns[id*2].shul;
	ns[id].shur=ns[id*2+1].shur;
}
void build(int id,int l,int r){
	if(l==r){
		ns[id].data[0]=a[l];
		ns[id].xl[0]=ns[id].xr[0]=true;
		ns[id].shul=a[l];
		ns[id].shur=a[l];
		return;
	}
	int mid=(l+r)/2;
	build(id*2,l,mid);
	build(id*2+1,mid+1,r);
	pushup(id);
}
void set(int id,int l,int r,int wei,int zhi){
	if(l==r){
		ns[id].data[0]=zhi;
		ns[id].shul=zhi;
		ns[id].shur=zhi;
		return;
	}
	int mid=(l+r)/2;
	if(wei<=mid){
		set(id*2,l,mid,wei,zhi);
	}
	else{
		set(id*2+1,mid+1,r,wei,zhi);
	}
	pushup(id);
}
signed main(){
	scanf("%lld%lld",&n,&q);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	build(1,1,n);
	while(q--){
		int x,y;
		scanf("%lld%lld",&x,&y);
		set(1,1,n,x,y);
		printf("%lld\n",ns[1].data[0]); 
	}
	return 0;
}

(T4)lnsyoj2211 通天之塔/P10652 「ROI 2017 Day 1」前往大都会

此题一共有两个问,可以分别求一下:
1.对于第一问,直接跑最短路即可。
2.对于第二问,可以使用dp解决,方法如下:
\(f_i\)为到i点时e的和的最大值
可得\(f_i=f_j+(dis_i-dis_j)^2\)
利用斜率优化dp,经过化简得\(f_{i}-dis_{i}^{2}=f_{j}+dis_{j}^{2}-2dis_{i}dis_{j}\)
由此得\(\begin{cases}x=dis_{j}\\y=f_{j}+dis_{j}^{2}\\k=2dis_{i}\\b=f_{i}-dis_{i}^{2}\end{cases}\)
因为要使\(f_i\)最大,所以应该使\(b\)最大,所以维护上凸包。
考虑\(x\)\(k\)均单调递增,所以维护单调栈。
对于本题需要先建一个最短路图(\(dis_{u}+w_{u,v}=dis_{v}\)的边所连成的图),然后对于每个火车线分别维护一个单调栈(坑点:对于在同一条火车线但被分成两段的边需要分别开栈)。
对于所有点需要按\(dis\)进行排序,保证\(x\)\(k\)单调,然后对于每个点,先通过经过这个点的所有铁路线算出这个点的dp,再把这个点放到经过这个点的每个铁路所维护的凸包中。
最后得出答案。
(实际不用新建图,只需要记录一下每个点被哪条铁路线所经过即可)
代码如下:

#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm> 
#define int long long
using namespace std;
struct Edge{
	int next;
	int to;
	int w;
	//int vi;
	int u;
}es[2000005];
int head[1000005],cnt;
vector<int> vi[1000005];
vector<int> nr[1000005];
inline void add(int u,int v,int w,int vi1){
	es[cnt].next=head[u];
	es[cnt].to=v;
	es[cnt].w=w;
	es[cnt].u=u;
	vi[vi1].push_back(cnt);
	head[u]=cnt;
	cnt++;
}
int n,m;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > pq;
struct ys{
	int dis;
	int id;
}yss[1000005];
bool cmp1(ys a,ys b){
	return a.dis<b.dis;
}
bool cmp2(ys a,ys b){
	return a.id<b.id;
}
bool vis[1000005];
int dp[1000005];
vector<int> stas[1000005];
vector<int> cov[1000005];
inline int calc(int i,int j){//yss编号 
	return dp[yss[j].id]+(yss[i].dis-yss[j].dis)*(yss[i].dis-yss[j].dis);
}
inline long double slope(int a,int b){//yss编号 
	return 1.0*(dp[yss[a].id]+yss[a].dis*yss[a].dis-dp[yss[b].id]-yss[b].dis*yss[b].dis)/\
		   (long double)(yss[a].dis-yss[b].dis);
}
int cntv;
signed main(){
	cnt=1;
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=m;i++){
		int l,u,v,w;
		scanf("%lld",&l);
		scanf("%lld",&u);
		for(int j=1;j<=l;j++){
			scanf("%lld",&w);
			scanf("%lld",&v);
			add(u,v,w,i);
			u=v;
		}
	}
	for(int i=1;i<=n;i++){
		yss[i].dis=0x3f3f3f3f3f3f3f3f;
		yss[i].id=i;
	}
	yss[1].dis=0;
	pq.push({0,1});
	while(!pq.empty()){
		int u=pq.top().second;
		pq.pop();
		if(vis[u]){
			continue;
		}
		vis[u]=true;
		for(int i=head[u];i;i=es[i].next){
			int v=es[i].to;
			if(yss[u].dis+es[i].w<yss[v].dis){
				yss[v].dis=yss[u].dis+es[i].w;
				pq.push({yss[v].dis,v});
			}
		}
	}
	for(int i=1;i<=m;i++){
		for(int j=0;j<vi[i].size();j++){
			if(yss[es[vi[i][j]].u].dis+es[vi[i][j]].w==yss[es[vi[i][j]].to].dis){
				nr[i].push_back(vi[i][j]);
			}
		}
	}
	for(int i=1;i<=m;i++){
		if(!nr[i].empty()){
			cntv++;
			cov[es[nr[i][0]].u].push_back(cntv);
			cov[es[nr[i][0]].to].push_back(cntv);
		}
		for(int j=1;j<nr[i].size();j++){
			if(es[nr[i][j]].u!=es[nr[i][j-1]].to){
				cntv++;
				cov[es[nr[i][j]].u].push_back(cntv);
			}
			cov[es[nr[i][j]].to].push_back(cntv);
		}
	}
	sort(yss+1,yss+n+1,cmp1);
	for(int i=1;i<=n;i++){
		for(int j=0;j<cov[yss[i].id].size();j++){
			if(stas[cov[yss[i].id][j]].empty()){
				continue;
			}
			while(stas[cov[yss[i].id][j]].size()>1 && \
				  calc(i,stas[cov[yss[i].id][j]][stas[cov[yss[i].id][j]].size()-1])<\
				  calc(i,stas[cov[yss[i].id][j]][stas[cov[yss[i].id][j]].size()-2])){
				stas[cov[yss[i].id][j]].pop_back();
			}
			dp[yss[i].id]=max(dp[yss[i].id],calc(i,stas[cov[yss[i].id][j]].back()));
		}
		for(int j=0;j<cov[yss[i].id].size();j++){
			while(stas[cov[yss[i].id][j]].size()>1 && \
				  slope(i,stas[cov[yss[i].id][j]][stas[cov[yss[i].id][j]].size()-1])>\
				  slope(stas[cov[yss[i].id][j]][stas[cov[yss[i].id][j]].size()-1],stas[cov[yss[i].id][j]][stas[cov[yss[i].id][j]].size()-2])){
				stas[cov[yss[i].id][j]].pop_back();
			}
			stas[cov[yss[i].id][j]].push_back(i);
		}
	}
	sort(yss+1,yss+n+1,cmp2);
	printf("%lld %lld\n",yss[n].dis,dp[n]);
	return 0;
}
posted on 2024-07-25 16:26  jisuheng123  阅读(90)  评论(0编辑  收藏  举报