• 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;
    }
    
  • 相关阅读:
    微擎 关注发送推荐消息
    修改 processor.php 文件,监听用户对该应用的消息
    微擎返回图文信息
    微擎客服消息发送的函数
    mui 底部导航栏的实现
    两张表的关联查询
    微擎 截取字符串
    webStorm中NodeJs 没有智能提示
    OpenCV 实现自己的线性滤波器
    OpenCV 基本的阈值操作
  • 原文地址:https://www.cnblogs.com/amagaisite/p/15986652.html
Copyright © 2020-2023  润新知