ARC076 Solution Set

前面两个 ABC 级别题没做。

C. Reconciled?

犬猿の仲。

注意到狗和猴不能放一块儿,那显然只能隔一个放一个。记两者数量为 \(n,m\),那么显然 \(|n-m|\leq 1\)。否则答案为 \(0\)

首先狗和猴可以分别乱排,方案数为 \(n!m!\)

接下来,如果 \(n=m\),那么狗和猴可以整体换位置,答案再多乘个 \(2\) 就好了。

/*
他决定要“格”院子里的竹子。于是他搬了一条凳子坐在院子里,面对着竹子硬想了七天,结果因为头痛而宣告失败。
DONT NEVER AROUND . //
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read()
{
	int x=0;
	char c=getchar();
	while(c<'0' || c>'9')	c=getchar();
	while(c>='0' && c<='9')	x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
	return x;
}
void write(int x)
{
	if(x>9)	write(x/10);
	putchar(x%10+'0');
}
const int MOD=1e9+7;
inline int Add(int u,int v){return u+v>=MOD?u+v-MOD:u+v;}
inline int Sub(int u,int v){return u-v>=0?u-v:u-v+MOD;}
inline int Mul(int u,int v){return LL(u)*LL(v)%MOD;}
int QuickPow(int x,int p)
{
	if(p<0)	p+=MOD-1;
	int ans=1,base=x;
	while(p)
	{
		if(p&1)	ans=Mul(ans,base);
		base=Mul(base,base);
		p>>=1;
	}
	return ans;
}
inline int Abs(int x){return x>0?x:-x;}
int n,m,fac[100005];
int main(){
	fac[0]=1;
	for(int i=1;i<=100000;++i)	fac[i]=Mul(fac[i-1],i);
	n=read(),m=read();
	if(Abs(n-m)>1)	return puts("0")&0;
	if(n<m)	swap(n,m);
	if(n==m)	write(Mul(Mul(fac[n],fac[n]),2));
	else	write(Mul(fac[n],fac[m]));
	return 0;
}

D. Built?

比较显然,对于 \(i,j,k\) 满足 \(x_i < x_j < x_k\),我不可能连边 \((i,k)\),因为连 \((i,j),(j,k)\) 价格不变且更优。

那么按 \(x,y\) 连一下,最小生成树就好了。

/*
他决定要“格”院子里的竹子。于是他搬了一条凳子坐在院子里,面对着竹子硬想了七天,结果因为头痛而宣告失败。
DONT NEVER AROUND . //
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
LL read()
{
	LL x=0;
	char c=getchar();
	while(c<'0' || c>'9')	c=getchar();
	while(c>='0' && c<='9')	x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
	return x;
}
void write(LL x)
{
	if(x>9)	write(x/10);
	putchar(x%10+'0');
}
struct node{
	LL x,id;
	node(){}
	node(LL X,LL I){x=X,id=I;}
	bool operator < (node ano) const {return x<ano.x;}
}X[100005],Y[100005];
struct unionFindSet{
	LL fa[100005];
	void makeSet(LL up){for(LL i=0;i<=up;++i)	fa[i]=i;}
	LL findSet(LL x){return fa[x]==x?x:fa[x]=findSet(fa[x]);}
	bool unionSet(LL x,LL y)
	{
		LL xx=findSet(x),yy=findSet(y);
		if(xx==yy)	return false;
		fa[yy]=xx;
		return true;
	}
}ufs;
struct Edge{
	LL u,v,w;
	Edge(){}
	Edge(LL U,LL V,LL W){u=U,v=V,w=W;}
	bool operator < (Edge ano) const {return w<ano.w;}
}ed[200005];
LL n,m,cnt;
LL Kruskal()
{
	ufs.makeSet(n);
	sort(ed+1,ed+1+m);
	LL ans=0;
	for(LL i=1;i<=m;++i)
	{
		LL u=ed[i].u,v=ed[i].v,w=ed[i].w;
		if(ufs.unionSet(u,v))	ans+=w;
	}
	return ans;
}
int main(){
	n=read();
	for(LL i=1;i<=n;++i)
	{
		LL x=read(),y=read();
		X[i]=node(x,i),Y[i]=node(y,i);
	}
	sort(X+1,X+1+n),sort(Y+1,Y+1+n);
	for(LL i=2;i<=n;++i)
	{
		ed[++cnt]=Edge(X[i-1].id,X[i].id,X[i].x-X[i-1].x);
		ed[++cnt]=Edge(Y[i-1].id,Y[i].id,Y[i].x-Y[i-1].x);
	}
	m=cnt;
	write(Kruskal());
	return 0;
}

E. Connected?

显然只有两个端点都在边界上才会影响答案。

那么,我们把边界展开抽成一个序列,相当于有 \(O(n)\) 种括号,问是否是一个合法的括号串。乱做就好了。

/*
他决定要“格”院子里的竹子。于是他搬了一条凳子坐在院子里,面对着竹子硬想了七天,结果因为头痛而宣告失败。
DONT NEVER AROUND . //
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
LL read()
{
	LL x=0;
	char c=getchar();
	while(c<'0' || c>'9')	c=getchar();
	while(c>='0' && c<='9')	x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
	return x;
}
void write(LL x)
{
	if(x>9)	write(x/10);
	putchar(x%10+'0');
}
LL n,R,C;
LL cnt;
inline bool onEdge(LL x,LL y){return x==0 || y==0 || x==R || y==C;}
#define mp make_pair
pair<LL,LL> brk[100005];
LL tmp[200005];
inline LL getId(LL x,LL y)
{
	if(x==0)	return y;
	else if(y==C)	return C+1e10+x;
	else if(x==R)	return C+C-y+2e11+R;
	else	return 8e12+C-x;
}
pair<LL,LL> pos[200005];
bool vis[100005];
int main(){
	R=read(),C=read(),n=read();
	for(LL i=1;i<=n;++i)
	{
		LL xp=read(),yp=read(),xq=read(),yq=read();
		if(!onEdge(xp,yp) || !onEdge(xq,yq))
		{
			--n,--i;
			continue;
		}
		tmp[2*i-1]=getId(xp,yp),tmp[2*i]=getId(xq,yq);
		brk[i]=mp(getId(xp,yp),getId(xq,yq));
	}
	sort(tmp+1,tmp+1+n+n);
	for(LL i=1;i<=n;++i)
	{
		brk[i].first=lower_bound(tmp+1,tmp+1+n+n,brk[i].first)-tmp;
		brk[i].second=lower_bound(tmp+1,tmp+1+n+n,brk[i].second)-tmp;
		if(brk[i].first>brk[i].second)	swap(brk[i].first,brk[i].second);
	}
	for(LL i=1;i<=n;++i)
	{
		pos[brk[i].first]=mp(i,0);
		pos[brk[i].second]=mp(i,1);
	}
	stack<LL> S;
	for(LL i=1;i<=2*n;++i)
	{
		if(!vis[pos[i].first])	S.push(pos[i].first),vis[pos[i].first]=true;
		else
		{
			if(S.top()!=pos[i].first)	return puts("NO")&0;
			S.pop();
		}
	}
	puts("YES");
	return 0;
}

F. Exhausted?

显然原问题就是问一开始最多能坐下多少个人。首先可以用反悔贪心,但是懒得做贪心咋办啊?

根据 Hall 定理,一个二分图的最大匹配是 \(\displaystyle |S| - \max_{T ⊆ S}\{ |T| - C(T) \}\),其中 \(C(T)\) 表示与 \(T\) 邻接的结点集合。

回到这个问题。我们直接连边会太多边了……并且是两个区间的并,然后再一起取交,显然不优美。我们取两个区间并的补集再取交就好看多了。

那我们要做的是,找到一个集合 \(T\),最大化:

\[|T| - (m - |∩_{v \in T} (l_i,r_i)|) \]

简单做一下之后,发现我们要最大化 \(|∩_{v \in T} (l_i,r_i)|\)。考虑交的左端点为 \(l\) 时,这个东西的值。假设交为 \((l,r)\),那么其值就是 \(r-l+\sum_{j}[l_j \leq l][r_j \geq r]\)。考虑构建线段树,先把第 \(i\) 个叶子的值赋上 \(i\) 处理掉 \(r\)\(-l\) 可以在我们处理的时候做。考虑扫描线保证 \(l_j \leq l\),这个时候我们只要找值最大的 \(r\) 就好。显然 \(l\) 一定是某个 \(l_i\),那么我们要在 \([l_i,r_i]\) 找到一个值最大的 \(r\) 就好了。线段树维护区间加区间最大值,随便做。

/*
他决定要“格”院子里的竹子。于是他搬了一条凳子坐在院子里,面对着竹子硬想了七天,结果因为头痛而宣告失败。
DONT NEVER AROUND . //
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read()
{
	int x=0;
	char c=getchar();
	while(c<'0' || c>'9')	c=getchar();
	while(c>='0' && c<='9')	x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
	return x;
}
void write(int x)
{
	if(x>9)	write(x/10);
	putchar(x%10+'0');
}
struct Segment{
	int l,r;
	inline void Scan(){l=read()+1,r=read()-1;}
	inline bool operator < (Segment ano) const {return l<ano.l || (l==ano.l && r<ano.r);}
}p[200005];
int n,m;
#define lc(x) (x<<1)
#define rc(x) (lc(x)|1)
#define Mm int mid=(l+r)>>1
int tag[800005],maxn[800005];
inline void push_up(int now){maxn[now]=max(maxn[lc(now)],maxn[rc(now)]);}
void build(int l,int r,int now)
{
	if(l==r)	return void(maxn[now]=l);
	Mm;
	build(l,mid,lc(now)),build(mid+1,r,rc(now));
	push_up(now);
}
inline void push_down(int now)
{
	if(tag[now])
	{
		tag[lc(now)]+=tag[now];
		tag[rc(now)]+=tag[now];
		maxn[lc(now)]+=tag[now];
		maxn[rc(now)]+=tag[now];
		tag[now]=0;
	}
}
void modify(int l,int r,int now,int x,int y)
{
	if(x>y)	return ;
	if(x<=l && r<=y)
	{
		++tag[now];
		++maxn[now];
		return ;
	}
	Mm;
	push_down(now);
	if(x<=mid)	modify(l,mid,lc(now),x,y);
	if(mid<y)	modify(mid+1,r,rc(now),x,y);
	push_up(now);
}
int query(int l,int r,int now,int x,int y)
{
	if(x>y)	return -2e7;
	if(x<=l && r<=y)	return maxn[now];
	Mm,ans=0;
	push_down(now);
	if(x<=mid)	ans=max(ans,query(l,mid,lc(now),x,y));
	if(mid<y)	ans=max(ans,query(mid+1,r,rc(now),x,y));
	return ans;
}
#undef lc
#undef rc
#undef Mm
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;++i)	p[i].Scan();
	sort(p+1,p+1+n);
	int ans=max(n-m,0);
	build(1,m,1);
	for(int i=1;i<=n;++i)
	{
		modify(1,m,1,1,p[i].r);
		ans=max(ans,query(1,m,1,p[i].l,p[i].r)-p[i].l+1-m);
	}
	write(ans);
	return 0;
}
posted @ 2022-03-09 19:19  SyadouHayami  阅读(27)  评论(0编辑  收藏  举报

My Castle Town.