noip模拟40 solutions

怎么说呢,我这两天用单调指针用的非常的顺手

第一题证都没证就单调指针去了,导致我抱零了

所以我以后用之前一定要证一下,

关于我做题老是想不到正解这件事,我已经不纠结了,因为我完全可以乱搞出奇迹

因为第二题我乱搞+卡时,搞到了70pts

T1 送花

这个就是枚举右端点,对左区间进行更新,这个有得天独厚的优势

因为一旦出现重复的东西,我就直接不要这个贡献了

在线段树上的操作就是对上上次出现的到上次出现的区间减去贡献

在上次出现到这次出现的区间内加上贡献

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=1e6+5;
int n,m,c[N];
ll d[N];
struct XDS{
	#define ls x<<1
	#define rs x<<1|1
	ll now[N*4],laz[N*4];
	void pushup(int x){
		now[x]=max(now[ls],now[rs]);
		return ;
	}
	void pushdown(int x){
		//cout<<x<<endl;
		if(!laz[x])return ;
		laz[ls]+=laz[x];
		laz[rs]+=laz[x];
		now[ls]+=laz[x];
		now[rs]+=laz[x];
		laz[x]=0;
		return ;
	}
	void ins(int x,int l,int r,int ql,int qr,ll v){
		//cout<<x<<" "<<l<<" "<<r<<" "<<ql<<" "<<qr<<endl;
		if(ql>r||qr<l||ql>qr)return ;
		if(ql<=l&&r<=qr){
			now[x]+=v;laz[x]+=v;
			return ;
		}
		pushdown(x);
		int mid=l+r>>1;
		if(ql<=mid)ins(ls,l,mid,ql,qr,v);
		if(qr>mid)ins(rs,mid+1,r,ql,qr,v);
		pushup(x);
		return ;
	}
	#undef ls
	#undef rs
}xds;
int pre[N],prx[N];
ll ans;
signed main(){
	scanf("%d%d",&n,&m);
	for(re i=1;i<=n;i++)scanf("%d",&c[i]);
	for(re i=1;i<=m;i++)scanf("%lld",&d[i]);
	for(re i=1;i<=n;i++){
		xds.ins(1,1,n,prx[c[i]]+1,pre[c[i]],-d[c[i]]);
		xds.ins(1,1,n,pre[c[i]]+1,i,d[c[i]]);
		prx[c[i]]=pre[c[i]];
		pre[c[i]]=i;
		ans=max(ans,xds.now[1]);
	}
	printf("%lld",ans);
}

T2 星空

这就是我考场出奇迹的题,直接乱搞得到70pts

具体是这样做的,我一眼看上去,这好像是个最短路诶

最短路的话,直接dij那不就是最好的选择嘛????

我直接把每一对点的距离放到队列里面,额,好像是魔改dij

然后每次提取队头,从这两个端点分别向外扩展n个点,得到新的距离,再放到队列中

这样的话,保守估计,复杂度应该是\(\mathcal{O(n^3log^3n)}\)

然后我觉得这样肯定会T ,所以就加上了一句话

int tim=clock();
if(clock()-tim>800000)break;

然后就成功的卡到了70pts,但是这个是有正确性的,如果数组可以开出来,就能A

因为每次都是找最短的来更新

70pts
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1e5+5;
struct node{
	int x,y,dis;
	node(){}
	node(int a,int b,int c){
		x=a;y=b;dis=c;
	}
	bool operator < (node a)const{
		return dis>a.dis;
	}
};
priority_queue<node> q;
int n,dit[6005][6005],ans,sum;
struct pot{
	int x,y;
	pot(){}
}sca[N];
bool vis[6005][6005];
signed main(){
	//cout<<((sizeof(vis)+sizeof(dit))>>20)<<endl;
	int tim=clock();
	scanf("%d",&n);
	for(re i=1;i<=n;i++)
		scanf("%d%d",&sca[i].x,&sca[i].y);
	memset(dit,0x3f,sizeof(dit));
	for(re i=1;i<=n;i++){
		for(re j=i+1;j<=n;j++){
			dit[j][i]=dit[i][j]=abs(abs(sca[i].x-sca[j].x)-abs(sca[i].y-sca[j].y));
			q.push(node(i,j,dit[i][j]));
		}
	}
	while(!q.empty()){
		if(clock()-tim>800000)break;
		int x=q.top().x,y=q.top().y,dis=q.top().dis;q.pop();
		if(vis[x][y]||dit[x][y]!=dis)continue;
		vis[x][y]=true;
		for(re i=1;i<=n;i++){
			if(i==x||i==y)continue;
			if(vis[i][y])continue;
			int tmp=abs(abs(sca[i].x-sca[x].x)-abs(sca[i].y-sca[x].y));
			if(tmp+dis<dit[i][y]){
				dit[y][i]=dit[i][y]=tmp+dis;
				q.push(node(i,y,dit[i][y]));
			}
		}
		for(re i=1;i<=n;i++){
			if(i==x||i==y)continue;
			if(vis[x][i])continue;
			int tmp=abs(abs(sca[i].x-sca[y].x)-abs(sca[i].y-sca[y].y));
			if(tmp+dis<dit[x][i]){
				dit[x][i]=dit[i][x]=tmp+dis;
				q.push(node(i,x,dit[x][i]));
			}
		}
	}
	ans=0x3f3f3f3f;
	for(re i=1;i<=n;i++)
		for(re j=i+1;j<=n;j++)
			if(dit[i][j])ans=min(ans,dit[i][j]);
	for(re i=1;i<=n;i++)
		for(re j=i+1;j<=n;j++)
			if(dit[i][j]==ans)sum++;
	printf("%d\n%d",ans,sum);
}

其实正解是先转换坐标系,给它斜过来,然后就像题解说的做就好了,

并查集真的挺好用的,然后用vector维护距离为ans1的并查集就行了

转化坐标系是真的妙,自己手摸一下就有了

noip模拟40[双指针被抛弃了....]_i++AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1e5+5;
int n;
struct node{
	int xp,yp,x,y,id;
	node(){}
}sca[N];
struct BSU{
	int fa[N],siz[N];
	BSU(){for(re i=1;i<=100000;i++)fa[i]=i,siz[i]=1;}
	int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
	void merge(int x,int y){
		if(!x||!y)return ;
		int fx=find(x),fy=find(y);
		if(fx==fy)return ;
		fa[fx]=fy;siz[fy]+=siz[fx];
	}
}bsu;
int mpx[N*4],mpy[N*4],bas=2e5+1,ans1=0x3f3f3f3f,ans2=0;
bool cmx(node a,node b){if(a.x!=b.x)return a.x<b.x;return a.y<b.y;}
bool cmy(node a,node b){if(a.y!=b.y)return a.y<b.y;return a.x<b.x;}
vector<pair<int,int> > vec[N*4];
signed main(){
	scanf("%d",&n);
	for(re i=1;i<=n;i++){
		scanf("%d%d",&sca[i].xp,&sca[i].yp);
		sca[i].x=sca[i].xp+sca[i].yp;
		sca[i].y=sca[i].xp-sca[i].yp;
		sca[i].id=i;
	}
	for(re i=1;i<=n;i++){
		int xx=sca[i].x+bas,yy=sca[i].y+bas;
		if(mpx[xx])bsu.merge(i,mpx[xx]);
		else mpx[xx]=i;
		if(mpy[yy])bsu.merge(i,mpy[yy]);
		else mpy[yy]=i;
	}
	sort(sca+1,sca+n+1,cmx);
	for(re i=1;i<n;i++){
		int tmp=min(abs(sca[i].x-sca[i+1].x),abs(sca[i].y-sca[i+1].y));
		int fx=bsu.find(sca[i].id),fy=bsu.find(sca[i+1].id);
		vec[tmp].push_back(make_pair(min(fx,fy),max(fx,fy)));
		if(tmp)ans1=min(ans1,tmp);
	}
	sort(sca+1,sca+n+1,cmy);
	for(re i=1;i<n;i++){
		int tmp=min(abs(sca[i].x-sca[i+1].x),abs(sca[i].y-sca[i+1].y));
		int fx=bsu.find(sca[i].id),fy=bsu.find(sca[i+1].id);
		vec[tmp].push_back(make_pair(min(fx,fy),max(fx,fy)));
		if(tmp)ans1=min(ans1,tmp);
	}
	printf("%d\n",ans1);
	sort(vec[ans1].begin(),vec[ans1].end());
	vec[ans1].erase(unique(vec[ans1].begin(),vec[ans1].end()),vec[ans1].end());
	//cout<<vec[ans1].size()<<endl;
	for(re i=0;i<vec[ans1].size();i++){
		if(vec[ans1][i].first==vec[ans1][i].second)continue;
		//cout<<vec[ans1][i].first<<" "<<vec[ans1][i].second<<endl;
		ans2+=bsu.siz[vec[ans1][i].first]*bsu.siz[vec[ans1][i].second];
	}
	printf("%d\n",ans2);
}

T3 零一串

这个我也没有改过,因为,我懒得动了

直接把每个1的移动的序列搞出来

然后就用队列维护就好了,维护这个序列中0的相对位置

沽~~~~