[提高组互测] Day5

总结

这次没有挂分,不过还是被淡随切吊打了

任何题最基本的问题转化都要有,思维步骤都有共通之处,不要只会做序列题啊

把题想简单一点,相信自己的实力都可以切,毕竟我切过 \(3400\) 的题啊!

保留环节:感谢 \(\tt crashed\) 大佬的精心准备,虽然他的电脑因为蓝屏必须重新造数据

[CCO 2019] Sirtet

题目描述

点此看题

解法

\(\tt naive\) 的题意转化:我们求出每个点的下落距离就可以知道最终状态。

对于初始四联通的点集,我们把他们连接起来当作一个整体,那么它们的下落距离是相同的。

考虑对于每个有物体的格子找到它下面第一个物品,设它的颜色是 \(x\)(每个点集颜色相同):

  • 如果颜色相同,那么可以忽略。
  • 如果不存在这样一个物品,那么 $d_x\leq $ 到底部的距离。
  • 如果存在这样一个物品,设其颜色是 \(y\),那么 \(d_x\leq d_y+\) 它们的距离。

显然是一个差分约束问题,建出来图之后直接跑即可,比省选的那道差分约束板多了

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int M = 1000005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,a[M],b[M],c[M],tot,f[M];char s[M];
int dis[M],ans[M];
int id(int x,int y) {return m*(x-1)+y;}
struct edge
{
	int v,c,next;
}e[2*M];
struct node
{
	int u,c;
	bool operator < (const node &b) const
	{
		return c>b.c;
	}
};priority_queue<node> q;
void dfs(int x,int y)
{
	if(!a[id(x,y)] || b[id(x,y)]) return ;
	b[id(x,y)]=k;
	if(x>1) dfs(x-1,y);
	if(x<n) dfs(x+1,y);
	if(y>1) dfs(x,y-1);
	if(y<m) dfs(x,y+1);
}
void add(int u,int v,int c)
{
	e[++tot]=edge{v,c,f[u]},f[u]=tot;
}
void dijkstra()
{
	dis[k+1]=0;
	q.push(node{k+1,0});
	while(!q.empty())
	{
		int u=q.top().u;
		int w=q.top().c;q.pop();
		if(dis[u]<w) continue;
		for(int i=f[u];i;i=e[i].next)
		{
			int v=e[i].v,c=e[i].c;
			if(dis[v]>dis[u]+c)
			{
				dis[v]=dis[u]+c;
				q.push(node{v,dis[v]});
			}
		}
	}
}
int main()
{
	//freopen("tpt.in","r",stdin);
	//freopen("tpt.out","w",stdout);
	n=read();m=read();
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s+1);
		for(int j=1;j<=m;j++)
			a[id(i,j)]=(s[j]=='#');
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(a[id(i,j)] && !b[id(i,j)])
				k++,dfs(i,j);
	memset(dis,0x3f,sizeof dis);
	for(int i=n;i>=1;i--)
		for(int j=1;j<=m;j++) if(a[id(i,j)])
		{
			int w=b[id(i,j)],p=id(c[j],j);
			if(!c[j])//nothing down below
				add(k+1,w,n-i);
			else if(b[p]!=w)//not the same color
				add(b[p],w,c[j]-i-1);
			c[j]=i;
		}
	dijkstra();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			int x=id(i,j);
			if(a[x]) ans[id(i+dis[b[x]],j)]=a[x];
		}
	for(int i=1;i<=n;i++,puts(""))
		for(int j=1;j<=m;j++)
		{
			if(ans[id(i,j)]) putchar('#');
			else putchar('.');
		}
}

「ROI 2019 Day2」课桌

题目描述

点此看题

解法

这道题的结论很多都是感受出来得分,不给证明,现在对追求严谨的我真是一种煎熬啊

结论1:将学生按升高排序之后相邻两个配对最优。

结论2:把课桌升序排序之后按顺序分给学生最优。这里稍微解释一下,首先我们要除去课桌有包含的情况,那么按左端点排序之后满足左端点单增,右端点单增,这样就能区分顺序了。

那么直接把 \(m\) 组的相邻学生绑定起来称为一块,那么一共有 \(n\) 块学生,对于每一块可以单独做,计算每个课桌的代价时可以搞一个双指针,时间复杂度 \(O(nm+mk)\)

复杂度爆炸的原因是把每一块学生割裂开来了,利用第二个性质,每一块学生对应的最优决策点是单调不降的。这时候可以无脑上决策单调性分治了,每次求第 \(mid\) 块的最优匹配点,然后左右的决策点范围是 \([ql,mid]\)\([mid,qr]\),时间复杂度 \(O((k+nm)\log m)\)

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int M = 400005;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,ans,b[M];vector<int> a[M];
struct node
{
	int l,r;
	bool operator < (const node &b) const
	{
		return l<b.l;
	}
}s[M];
void cdq(int l,int r,int ql,int qr)
{
	if(l>r) return ;
	int x=(l+r)>>1;
	int pre=0,suf=0,j=0,k=0,res=1e15,mid=ql;
	int len=a[x].size(),nl=0,nr=len;
	for(auto y:a[x]) suf+=y;
	for(int i=ql;i<=qr;i++)
	{
		if(s[i].r<=s[i-1].r) continue;//useless
		while(j<len && a[x][j]<=s[i].l)
		{
			pre+=a[x][j];
			j++;nl++;
		}
		while(k<len && a[x][k]<s[i].r)
		{
			suf-=a[x][k];
			k++;nr--;
		}
		int tmp=(suf-s[i].r*nr)+(s[i].l*nl-pre);
		if(tmp<res) mid=i,res=tmp;
	}
	ans+=res;
	cdq(l,x-1,ql,mid);
	cdq(x+1,r,mid,qr);
}
signed main()
{
	m=read();n=read();k=read();
	for(int i=1;i<=k;i++)
		s[i].l=read(),s[i].r=read();
	sort(s+1,s+1+k);
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=2*n;j++)
			b[j]=read();
		sort(b+1,b+1+2*n);
		for(int j=1;j<=n;j++)
		{
			a[j].push_back(b[2*j-1]);
			a[j].push_back(b[2*j]);
		}
	}
	for(int i=1;i<=n;i++)
		sort(a[i].begin(),a[i].end());
	cdq(1,n,1,k);
	printf("%lld\n",ans);
}
posted @ 2021-10-15 17:07  C202044zxy  阅读(204)  评论(0编辑  收藏  举报