雅礼集训2017day2乱写

t1

先写了一发暴力做法:离散化高度,然后设\(f[i][j]\)表示到第\(i\)个位置,水位为\(j\)能满足的最多条件

如果\(j\le h(i-1,i)\),那么可以从低于\(h(i-1,i)\)的任何地方转移过来

如果\(j>h(i-1,i)\),那么\(f[i][j]\)只能从\(f[i-1][j]\)转移

搞一个前缀最大值,复杂度\(O(n^2)\)

正解就很神仙,我们可以按照高度从下向上遍历关键节点(条件和隔板的位置)

可以发现在高度不断升高的过程中,有些位置会合并成一个整体(水位超过隔板),这个过程可以用并查集维护

枚举高度\(h\),设两个数组\(ans,sum\)

其中\(sum[x]\)表示\(x\)所在联通块水位高度为\(h\)时,不考虑\(h\)上方的条件,能满足的条件数

\(ans[x]\)表示\(x\)所在联通块水位高度不超过\(h\)时,不考虑$h&上方的条件,能满足的最多的条件数

对于同一高度的信息,我们先处理隔板,再处理无水的条件,最后处理有水的条件

如果当前信息是有水的条件:那么只要\(++sum[x]\),同时更新\(ans[x]\)

如果当前信息是无水的条件:那么只要\(++ans[x]\),因为原本\(ans[x]\)是该水位以下的信息,所以这个条件一定能被选到

如果是隔板,就合并隔板两侧联通块的\(ans\)\(sum\)

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define y1 qwq 
	inline int read()
	{
		int x=0;char ch,f=1;
		for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
		if(ch=='-') f=0,ch=getchar();
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
		return f?x:-x;
	} 
	const int N=1e5+10,inf=1<<30;
	int haku,n,m;
	struct node
	{
		int x,y,z;
		inline bool operator < (const node &t) const
		{
			return y==t.y?z<t.z:y<t.y;
		}
	}a[N<<1];
	int f[N],ans[N],sum[N];
	inline int find(int k)
	{
		return f[k]==k?k:f[k]=find(f[k]);
	}
	inline void main()
	{
		haku=read();
		while(haku--)
		{
			n=read(),m=read();
			memset(ans,0,sizeof(ans));
			memset(sum,0,sizeof(sum));
			for(int i=1;i<n;++i) a[i].x=i,a[i].y=read(),a[i].z=-1;
			for(int i=1;i<=m;++i) a[i+n-1].x=read(),a[i+n-1].y=read(),a[i+n-1].z=read();
			for(int i=1;i<=n;++i) f[i]=i;
			sort(a+1,a+n+m);
			for(int i=1;i<n+m;++i)
			{
				if(a[i].z==-1)
				{
					int x=find(a[i].x),y=find(a[i].x+1);
					if(x==y) continue;
					f[x]=y;ans[y]+=ans[x];sum[y]+=sum[x];
				}
				if(a[i].z==0)
				{
					int x=find(a[i].x);
					++ans[x];
				}
				if(a[i].z==1)
				{
					int x=find(a[i].x);
					ans[x]=max(ans[x],++sum[x]);
				}
			}
			printf("%d\n",ans[find(1)]);
		}
	}
}
signed main()
{
	red::main();
	return 0;
}

t2

这种问题和数据范围很容易想到二分图和网络流那一堆东西。。

考虑落下一颗棋子,如果该棋子所在联通块是一个完美匹配的二分图,那么显然\(Bob\)可以每次通过走匹配边来让自己不败

如果该棋子所在位置不一定在二分图的最大匹配内,那么\(Bob\)移动一次之后一定会进入二分图最大匹配的边(且不存在交替路,因为他刚刚走的就是)

所以我们的任务其实就是将找到所有不一定存在于二分图最大匹配内的点

具体方法是找到所有不在二分图最大匹配内的点,然后从这些点出发走交替路

经过的左部节点都可以选择,因为已经经过的部分置换为起点到当前点的交替路,没经过过的部分可以不变

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define y1 qwq
#define id(x,y) (((x)-1)*m+(y))
	inline int read()
	{
		int x=0;char ch,f=1;
		for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
		if(ch=='-') f=0,ch=getchar();
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
		return f?x:-x;
	} 
	const int N=1e5+10,inf=1<<30;
	int n,m;
	char s[110][110]; 
	int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
	int head[N],cnt;
	struct point
	{
		int nxt,to;
		point(){}
		point(const int &nxt,const int &to):nxt(nxt),to(to){}
	}a[N<<1];
	inline void link(int x,int y)
	{
		a[++cnt]=(point){head[x],y};head[x]=cnt;
		a[++cnt]=(point){head[y],x};head[y]=cnt;
	}
	int st[N<<1],top;
	int f[N<<1],vis[N<<1],tim;
	bool ans[N<<1];
	inline bool find(int now)
	{
		for(int i=head[now];i;i=a[i].nxt)
		{
			int t=a[i].to;
			if(vis[t]==tim) continue;
			vis[t]=tim;
			if(!f[t]||find(f[t]))
			{
				f[t]=now;
				return 1;
			}
		}
		return 0;
	}
	inline void dfs(int now)
	{
		ans[now]=1;
		for(int i=head[now];i;i=a[i].nxt)
		{
			int t=a[i].to;
			if(f[t]&&!ans[f[t]]) dfs(f[t]);
		}
	}
	inline void main()
	{
		n=read(),m=read();
		for(int i=1;i<=n;++i) scanf("%s",s[i]+1);
		for(int i=1;i<=n;++i)
		{
			for(int j=1;j<=m;++j)
			{
				if(s[i][j]=='.')
				{
					for(int k=0;k<4;++k)
					{
						int tx=i+dx[k],ty=j+dy[k];
						if(s[tx][ty]!='.') continue;
						link(id(i,j),id(tx,ty)+n*m);
						link(id(tx,ty)+n*m,id(i,j)); 
					}
				}
			}
		}
		for(int i=1;i<=n;++i)
		{
			for(int j=1;j<=m;++j)
			{
				if(s[i][j]=='.')
				{
					++tim;
					if(!find(id(i,j))) st[++top]=id(i,j);
				}
			}
		}
		if(!top) return(void)puts("LOSE");
		for(int i=1;i<=top;++i) dfs(st[i]);
		top=0;
		for(int i=1;i<=n*m;++i)
			if(ans[i]) st[++top]=i;
		if(!top) return(void)puts("LOSE");
		puts("WIN");
		for(int i=1;i<=top;i++) printf("%d %d\n",(st[i]-1)/m+1,(st[i]-1)%m+1); 
	}
}
signed main()
{
	red::main();
	return 0;
}

t3

李超树模板题,调了\(4\)个小时我心态爆炸呜呜呜呜呜

我的写法实在太丑了就不放了,去网上找别人的板子吧QAQ

猜你喜欢

转载自www.cnblogs.com/knife-rose/p/12741899.html