首页 > 其他分享 >多校A层冲刺NOIP2024模拟赛07

多校A层冲刺NOIP2024模拟赛07

时间:2024-10-15 18:02:20浏览次数:1  
标签:rt cnt 07 int ll 多校 ans sum NOIP2024

多校A层冲刺NOIP2024模拟赛07

\(T1\) A. 限速(speed) \(40pts\)

  • 设最终保留的边的权值构成的集合为 \(S\) 。那么其贡献为 \(\begin{cases} k-\max\limits_{x \in S}\{ x \} & \max\limits_{x \in S}\{ x \} \le k \\ \sum\limits_{x \in S}[x>k] \times (x-k) & \max\limits_{x \in S}\{ x \}>k \end{cases}\) 。

  • 两种情况一起考虑贡献不太好做,考虑分开统计。

  • 若仅用边权 \(\le k\) 的边无法使整张图连通,则类似 \(Kruskal\) 的写法逐个将边权 \(>k\) 的边加入其中,直接统计贡献。

  • 若仅用边权 \(\le k\) 的边可以使整张图连通,又分为两种情况。

    • 若仅用边权 \(\le k\) 的边,则直接统计贡献。
    • 否则可以证明最多加入一条边权 \(>k\) 的边(且任意一条边均可以)替换一条边权 \(\le k\) 的边从而使得图连通,得到的贡献和上个贡献取 \(\min\) 。
    点击查看代码
    struct node
    {
    	ll from,to,w;
    }e[200010];
    bool cmp(node a,node b)
    {
    	return a.w<b.w;
    }
    struct DSU
    {
    	ll fa[200010];
    	void init(ll n)
    	{
    		for(ll i=1;i<=n;i++)
    		{
    			fa[i]=i;
    		}
    	}
    	ll find(ll x)
    	{
    		return (fa[x]==x)?x:fa[x]=find(fa[x]);
    	}
    }D;
    int main()
    {
    	freopen("speed.in","r",stdin);
    	freopen("speed.out","w",stdout);
    	ll n,m,k,len,maxx=0,sum=0,cnt=0,ans=0,x,y,i;
    	cin>>n>>m>>k;
    	len=m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>e[i].from>>e[i].to>>e[i].w;
    	}
    	sort(e+1,e+1+m,cmp);
    	for(i=1;i<=m;i++)
    	{
    		if(e[i].w>k)
    		{
    			len=i-1;
    			break;
    		}
    	}
    	D.init(n);
    	for(i=len;i>=1;i--)
    	{
    		x=D.find(e[i].from);
    		y=D.find(e[i].to);
    		if(x!=y)
    		{
    			D.fa[x]=y;
    			cnt++;
    			maxx=max(maxx,e[i].w);
    		}
    	}
    	if(cnt==n-1)
    	{
    		ans=k-maxx;
    		if(len+1<=m)
    		{
    			ans=min(ans,e[len+1].w-k);
    		}
    	}
    	else
    	{
    		for(i=len+1;i<=m;i++)
    		{
    			x=D.find(e[i].from);
    			y=D.find(e[i].to);
    			if(x!=y)
    			{
    				D.fa[x]=y;
    				sum+=e[i].w-k;
    			}
    		}
    		ans=sum;
    	}
    	cout<<ans<<endl;
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

\(T2\) B. 酒鬼 (drunkard) \(18pts\)

  • 部分分

    • 子任务 \(1\) :先走到 \(q_{i}\) ,然后开始左右移动从而拖延时间,故 \((q_{i}-p_{i}+1) \mod 2\) 即为所求。
    • 子任务 \(2\) :直接走到 \(q_{i}\) ,故 \(q_{i}-p_{i}+1\) 即为所求。
    点击查看代码
    string s;
    int main()
    {
    	freopen("drunkard.in","r",stdin);
    	freopen("drunkard.out","w",stdout);
    	int n,m,p,q,i;
    	cin>>n>>m;
    	if(m==2)
    	{
    		cin>>s>>p>>q;
    		cin>>s;
    		if(s=="min")
    		{
    			if((q-(p-1))%2==0)
    			{
    				cout<<0<<endl;
    			}
    			else
    			{
    				cout<<1<<endl;
    			}
    		}
    		else
    		{
    			cout<<q-(p-1)<<endl;
    		}
    	}
    	else
    	{
    		for(i=1;i<=m;i++)
    		{
    			cin>>s;
    			if(s=="clue")
    			{
    				cin>>p>>q;
    			}
    			else
    			{
    				cout<<"bad"<<endl;
    			}
    		}
    	}
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

\(T3\) C. 距离(distance) \(70pts\)

  • 部分分
    • \(40pts\) :模拟,会被链卡到飞起,时间复杂度为 \(O(n^{3})\) 。

      点击查看代码
      const int p=1000000007;
      struct node
      {
      	int nxt,to;
      }e[1000010];
      int head[1000010],dfn[1000010],out[1000010],pos[1000010],a[1000010],b[1000010],cnt=0,tot=0;
      void add(int u,int v)
      {
      	cnt++;
      	e[cnt].nxt=head[u];
      	e[cnt].to=v;
      	head[u]=cnt;
      }
      void dfs(int x,int fa)
      {
      	tot++;
      	dfn[x]=tot;
      	pos[tot]=x;
      	for(int i=head[x];i!=0;i=e[i].nxt)
      	{
      		if(e[i].to!=fa)
      		{
      			dfs(e[i].to,x);
      		}
      	}
      	out[x]=tot;
      }
      int qadd(int a,int b,int p)
      {
      	return a+b>=p?a+b-p:a+b;
      }
      int main()
      {
      	freopen("distance.in","r",stdin);
      	freopen("distance.out","w",stdout);
      	int n,u,v,ans=0,i,j,k;
      	scanf("%d",&n);
      	for(i=1;i<=n-1;i++)
      	{
      		scanf("%d%d",&u,&v);
      		add(u,v);
      		add(v,u);
      	}
      	for(i=1;i<=n;i++)
      	{
      		scanf("%d%d",&a[i],&b[i]);
      	}
      	dfs(1,0);
      	for(i=1;i<=n;i++)
      	{
      		ans=0;
      		for(j=dfn[i];j<=out[i];j++)
      		{
      			for(k=dfn[i];k<=out[i];k++)
      			{
      				ans=qadd(ans,min(abs(a[pos[j]]-a[pos[k]]),abs(b[pos[j]]-b[pos[k]])),p);
      			}
      		}
      		printf("%d\n",ans);
      	}
      	fclose(stdin);
      	fclose(stdout);
      	return 0;
      }
      
    • \(70pts\) :子树信息可以直接合并,每对点对之间的贡献只会被算一次,随便写个什么维护即可,时间复杂度为 \(O(n^{2})\) 。

      点击查看代码
      const int p=1000000007;
      struct node
      {
      	int nxt,to;
      }e[1000010];
      int head[500010],dfn[500010],out[500010],pos[500010],a[500010],b[500010],ans[500010],cnt=0,tot=0;
      vector<short>rt[10010];
      void add(int u,int v)
      {
      	cnt++;
      	e[cnt].nxt=head[u];
      	e[cnt].to=v;
      	head[u]=cnt;
      }
      void merge(int x,int y)
      {
      	if(rt[x].size()<rt[y].size())
      	{
      		swap(rt[x],rt[y]);
      	}
      	for(int i=0;i<rt[y].size();i++)
      	{
      		rt[x].push_back(rt[y][i]);
      	}
      }
      int qadd(int a,int b,int p)
      {
      	return a+b>=p?a+b-p:a+b;
      }
      void dfs(int x,int fa)
      {
      	tot++;
      	dfn[x]=tot;
      	pos[tot]=x;
      	rt[x].push_back(x);
      	for(int i=head[x];i!=0;i=e[i].nxt)
      	{
      		if(e[i].to!=fa)
      		{
      			dfs(e[i].to,x);
      			ans[x]=qadd(ans[x],ans[e[i].to],p);
      			for(int j=0;j<rt[e[i].to].size();j++)
      			{
      				for(int k=0;k<rt[x].size();k++)
      				{
      					ans[x]=qadd(ans[x],min(abs(a[rt[e[i].to][j]]-a[rt[x][k]]),abs(b[rt[e[i].to][j]]-b[rt[x][k]]))*2%p,p);
      				}
      			}
      			merge(x,e[i].to);
      		}
      	}
      	out[x]=tot;
      }
      int main()
      {
      	freopen("distance.in","r",stdin);
      	freopen("distance.out","w",stdout);
      	int n,u,v,i;
      	scanf("%d",&n);
      	for(i=1;i<=n-1;i++)
      	{
      		scanf("%d%d",&u,&v);
      		add(u,v);
      		add(v,u);
      	}
      	for(i=1;i<=n;i++)
      	{
      		scanf("%d%d",&a[i],&b[i]);
      	}
      	dfs(1,0);
      	for(i=1;i<=n;i++)
      	{
      		printf("%d\n",ans[i]);
      	}
      	fclose(stdin);
      	fclose(stdout);
      	return 0;
      }
      
  • 正解
    • 先将 \(\min(|a_{i}-a_{j}|,|b_{i}-b_{j}|)\) 容斥成 \(|a_{i}-a_{j}|+|b_{i}-b_{j}|-\max(|a_{i}-a_{j}|,|b_{i}-b_{j}|)\) ,难点在于怎么求后面的 \(\max(|a_{i}-a_{j}|,|b_{i}-b_{j}|)\) 。

    • 将 \(\{ a \}\) 看做横坐标, \(\{ b \}\) 看做纵坐标,等价于询问曼哈顿坐标系下以 \(x\) 为根的子树内的节点两两距离之和。

    • 考虑转化成曼哈顿距离,令 \(\forall i \in [i,n],p_{i}=\frac{a_{i}+b_{i}}{2},q_{i}=\frac{a_{i}-b_{i}}{2}\) ,则 \(\max\limits(|a_{i}-a_{j}|,|b_{i}-b_{j}|)=|p_{i}-p_{j}|+|q_{i}-q_{j}|\) ,等价于询问 \(\sum\limits_{i \in Subtree(x)}\sum\limits_{j \in Subtree(x)}|a_{i}-a_{j}|+|b_{i}-b_{j}|-|p_{i}-p_{j}|+|q_{i}-q_{j}|\) 。

      • 拆绝对值,有 \(\begin{aligned} &|p_{i}-p_{j}|+|q_{i}-q_{j}| \\ &=\max\limits(p_{i}-p_{j},p_{j}-p_{i})+\max\limits(q_{i}-q_{j},q_{j}-q_{i}) \\ &=\max(\frac{a_{i}+b_{i}-a_{j}-b_{j}}{2},\frac{a_{j}+b_{j}-a_{i}-b_{i}}{2})+\max(\frac{a_{i}-b_{i}-a_{j}+b_{j}}{2},\frac{a_{j}-b_{j}-a_{i}+b_{i}}{2}) \\ &=\max(a_{i}-a_{j},a_{j}-a_{i},b_{i}-b_{j},b_{j}-b_{i}) \\ &=\max(|a_{i}-a_{j}|,|b_{i}-b_{j}|) \end{aligned}\) 。
    • 以 \(\{ a \}\) 为例,用线段树对 \(\{ a \}\) 开一个桶数组。当插入一个新的数 \(a_{i}\) 时维护 \(<a_{i}, \ge a_{i}\) 的元素的数量及和,从而得到贡献。树上启发式合并的时间复杂度为 \(O(n \log^{2} n)\) ,因为 accoders NOI 和学校 \(OJ\) 上开了 \(7s/5s\) ,所以可以通过。

    • 线段树合并的时间复杂度为 \(O(n \log n)\) 。具体地,对于以 \(x\) 为根的子树内的节点的值构成的有序数组 \(\{ a \}\) ,设 \(ans_{l,r},sum_{l,r},cnt_{l,r}\) 分别表示 \([l,r]\) 的贡献/元素和/元素数量,则有 \(ans_{l,r}=ans_{l,mid}+ans_{mid+1,r}+sum_{mid+1,r} \times cnt_{1,mid}-sum_{1,mid} \times cnt_{mid+1,n}\) 。向上递归时 pushup 即可。

      点击查看代码
      const ll mod=1000000007;
      struct node
      {
      	int nxt,to;
      }e[1000010];
      int head[500010],cnt=0;
      ll a[500010],aa[500010],b[500010],bb[500010],p[500010],pp[500010],q[500010],qq[500010],ans[500010];
      void add(int u,int v)
      {
      	cnt++;
      	e[cnt].nxt=head[u];
      	e[cnt].to=v;
      	head[u]=cnt;
      }
      ll qpow(ll a,ll b,ll p)
      {
      	ll ans=1;
      	while(b)
      	{
      		if(b&1)
      		{
      			ans=ans*a%p;
      		}
      		b>>=1;
      		a=a*a%p;
      	}
      	return ans;
      }
      struct SMT
      {
      	int root[500010],rt_sum=0;
      	struct SegmentTree
      	{
      		int ls,rs;
      		ll sum,cnt,ans;
      	}tree[500010*32];
      	#define lson(rt) (tree[rt].ls)
      	#define rson(rt) (tree[rt].rs)
      	void clear()
      	{
      		rt_sum=0;
      		memset(root,0,sizeof(root));
      	}
      	int build()
      	{
      		rt_sum++;
      		lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=tree[rt_sum].cnt=tree[rt_sum].ans=0;
      		return rt_sum;
      	}
      	void pushup(int rt)
      	{
      		tree[rt].sum=(tree[lson(rt)].sum+tree[rson(rt)].sum)%mod;
      		tree[rt].cnt=(tree[lson(rt)].cnt+tree[rson(rt)].cnt)%mod;
      		tree[rt].ans=((tree[lson(rt)].ans+tree[rson(rt)].ans)%mod+tree[rson(rt)].sum*tree[lson(rt)].cnt%mod-tree[lson(rt)].sum*tree[rson(rt)].cnt%mod+mod)%mod;
      	}
      	void update(int &rt,int l,int r,int pos,int val)
      	{
      		rt=(rt==0)?build():rt;
      		if(l==r)
      		{
      			tree[rt].cnt=(tree[rt].cnt+1)%mod;
      			tree[rt].sum=(tree[rt].sum+val)%mod;
      			return;
      		}
      		int mid=(l+r)/2;
      		if(pos<=mid)
      		{
      			update(lson(rt),l,mid,pos,val);
      		}
      		else
      		{
      			update(rson(rt),mid+1,r,pos,val);
      		}
      		pushup(rt);
      	}
      	int merge(int rt1,int rt2,int l,int r)
      	{
      		if(rt1==0||rt2==0)
      		{
      			return rt1+rt2;
      		}
      		if(l==r)
      		{
      			tree[rt1].sum=(tree[rt1].sum+tree[rt2].sum)%mod;
      			tree[rt1].cnt=(tree[rt1].cnt+tree[rt2].cnt)%mod;
      			return rt1;
      		}
      		int mid=(l+r)/2;
      		lson(rt1)=merge(lson(rt1),lson(rt2),l,mid);
      		rson(rt1)=merge(rson(rt1),rson(rt2),mid+1,r);
      		pushup(rt1);
      		return rt1;
      	}
      }T;
      void dfs(int x,int fa,ll d,ll a[],ll aa[])
      {
      	for(int i=head[x];i!=0;i=e[i].nxt)
      	{
      		if(e[i].to!=fa)
      		{
      			dfs(e[i].to,x,d,a,aa);
      			T.root[x]=T.merge(T.root[x],T.root[e[i].to],1,aa[0]);
      		}
      	}
      	T.update(T.root[x],1,aa[0],a[x],aa[a[x]]);
      	ans[x]=(ans[x]+T.tree[T.root[x]].ans*d%mod+mod)%mod;
      }
      void solve(int n,ll d,ll a[],ll aa[])
      {
      	T.clear();
      	for(int i=1;i<=n;i++)
      	{
      		aa[i]=a[i];
      	}
      	sort(aa+1,aa+1+n);
      	aa[0]=unique(aa+1,aa+1+n)-(aa+1);
      	for(int i=1;i<=n;i++)
      	{
      		a[i]=lower_bound(aa+1,aa+1+aa[0],a[i])-aa;
      	}
      	for(int i=1;i<=aa[0];i++)
      	{
      		aa[i]=(aa[i]+mod)%mod;
      	}
      	dfs(1,0,d,a,aa);
      }	
      int main()
      {
      	freopen("distance.in","r",stdin);
      	freopen("distance.out","w",stdout);
      	int n,u,v,i;
      	ll inv=qpow(2,mod-2,mod);
      	scanf("%d",&n);
      	for(i=1;i<=n-1;i++)
      	{
      		scanf("%d%d",&u,&v);
      		add(u,v);
      		add(v,u);
      	}
      	for(i=1;i<=n;i++)
      	{
      		scanf("%lld%lld",&a[i],&b[i]);
      		p[i]=a[i]+b[i];
      		q[i]=a[i]-b[i];
      	}
      	solve(n,1,a,aa);
      	solve(n,1,b,bb);
      	solve(n,(mod-inv)%mod,p,qq);
      	solve(n,(mod-inv)%mod,q,pp);
      	for(i=1;i<=n;i++)
      	{
      		printf("%lld\n",ans[i]*2%mod);
      	}
      	fclose(stdin);
      	fclose(stdout);
      	return 0;
      }
      

\(T4\) D. 团队选拔(selection) \(8pts\)

  • 部分分

    • \(8pts\) :爆搜。
    点击查看代码
    const ll p=998244353;
    ll a[100010],vis[100010],ans[100010];
    ll qpow(ll a,ll b,ll p)
    {
    	ll ans=1;
    	while(b)
    	{
    		if(b&1)
    		{
    			ans=ans*a%p;
    		}
    		b>>=1;
    		a=a*a%p;
    	}
    	return ans;
    }
    ll gcd(ll a,ll b)
    {
    	return b?gcd(b,a%b):a;
    }
    void dfs(ll pos,ll n,ll pre)
    {
    	if(pos==n+1)
    	{
    		ll flag=1,pos=0,d=0,last=0;
    		for(ll i=1;i<=n;i++)
    		{
    			if(vis[i]!=0)
    			{
    				last=gcd(last,a[i]);
    				if(vis[i]!=vis[i+1])
    				{
    					pos=i;
    					break;
    				}
    			}
    		}
    		for(ll i=pos+1;i<=n;i++)
    		{
    			if(vis[i]!=0)
    			{
    				d=gcd(d,a[i]);
    				if(vis[i]!=vis[i+1])
    				{
    					flag&=(d==0||d==last);
    					d=0;
    				}
    			}
    			else
    			{
    				flag&=(d==0||d==last);
    				d=0;
    			}
    		}
    		if(flag==1)
    		{
    			for(ll i=1;i<=n;i++)
    			{
    				if(vis[i]!=0)
    				{
    					ans[i]=(ans[i]+1)%p;
    				}
    			}
    		}
    	}
    	else
    	{
    		if(vis[pos-1]!=0)
    		{
    			vis[pos]=vis[pos-1];
    			dfs(pos+1,n,pos);
    		}
    		vis[pos]=vis[pre]+1;
    		dfs(pos+1,n,pos);	
    		vis[pos]=0;
    		dfs(pos+1,n,pre);
    	}
    }
    int main()
    {
    	freopen("selection.in","r",stdin);
    	freopen("selection.out","w",stdout);
    	ll n,i;
    	scanf("%lld",&n);
    	for(i=1;i<=n;i++)
    	{
    		scanf("%lld",&a[i]);
    	}
    	dfs(1,n,1);
    	for(i=1;i<=n;i++)
    	{
    		printf("%lld ",ans[i]);
    	}
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

总结

  • \(T1\) 边权 $ \le k$ 的边界点初始值写错了,而且误认为 \(k-\max\limits_{x \in S}\{ x \}=\max\limits_{x \in S} \{ k-x \}\) ,挂了 \(60pts\) 。
  • \(T2\) 写完后以为原来代码搞反了 \(p,q\) 的含义遂反转了过来,结果是原来代码没写反,挂了 \(9pts\) 。
  • 写 \(T4\) 爆搜的时间用得太多了。

后记

  • \(T2\) 的大样例下发文件里夹杂着 \(T4\) 的大样例, \(T4\) 的大样例下发文件里不知道是个什么东西。
  • \(T1,T2,T4\) 均出自 [2024暑期交流小组] CSP-S & NOIP模拟赛 4 ,故直接搬的官方整个 PDF 题解; \(T3\) 单独下发了题解。

标签:rt,cnt,07,int,ll,多校,ans,sum,NOIP2024
From: https://www.cnblogs.com/The-Shadow-Dragon/p/18468096

相关文章

  • [ABC062C]/[ARC074A] Chocolate Bar 题解
    神秘分讨题(?总共\(4\)中情况。第一种:三个竖的并列:ans=min(ans,(h%3>0)*w);。第二种:三个横的并列:ans=min(ans,(w%3>0)*h);。第三种:一个横的矩形,然后是两个竖着的。For(i,1,h){ intfst=i*w; intscd=(w/2)*(h-i); intthd=(w%2>0)*(h-i)+(w/2)*(h-i); ans=min(ans......
  • DLT645-2007 协议快速入门
    @目录DLT645-2007协议快速入门1.什么是DLT645-2007协议2.帧格式2.1帧起始符2.2地址域2.3控制码3.4数据长度3.5数据域2.6校验码CS2.7结束符2.8传输事项3.报文解析4.代码实例5.报文解析工具DLT645-2007协议快速入门1.什么是DLT645-2007协议DLT645目前主要使用......
  • 统计数字(2007年NOIP全国联赛提高组)
    题目描述某次科研调查时得到了n个自然数,每个数均不超过1500000000(1.5*109)。已知不相同的数不超过10000个,现在需要统计这些自然数各自出现的次数,并按照自然数从小到大的顺序输出统计结果。输入格式每组输入数据包含n+1行;第一行是整数n,表示自然数的个数;第2~n+1行,每行一个自......
  • 2024牛客暑期多校训练营4 - J. Zero (究极卡常)
    \(O(N^2)\)AC。输入后预处理?数量的前缀和。双层循环找所有的区间\([l,r]\)使区间内没有\(0\),找到以后直接用逆元+快速幂求\(\frac{(r-l+1)^k}{2^{sum_{r}-sum_{l-1}}}\),最后累加和。因为数据过水,这样已经能AC了。#include<cstdio>usingnamespacestd;constint......
  • NOIP2024集训Day49 图论
    NOIP2024集训Day49图论A.[BZOJ2348中山市选2011]杀人游戏最优决策一定是我们找到一个点,使它能够尽可能到达更多的点,然后我们会发现必须询问的人缩点后就是入度为\(0\)的点。如果剩下了一个人,那么这个人是可以被推出来的。即:入度为\(0\)的点是一定要被询问的,如果存在一......
  • NOIP2024集训Day50 图论
    NOIP2024集训Day50图论A.[JSOI2012]越狱老虎桥先边双缩点,建出边双生成树。在不额外加边的情况下,割掉树边会使子树内部断开;在加入边的情况下,若加入一条\(1-u\)的边,则形成了一个\(1-u\)的环,环无法通过割一条边断开;而连接树上两个节点\((u,v)\)的情况,把图展开后发......
  • 云电脑玩赛博朋克2077必备三个条件,以ToDesk为例
    云电脑近期成为不少用户玩游戏的首选,尤其是面对像《赛博朋克2077》这样硬件要求高的游戏时,价格实惠且性能极高的云电脑,简直是游戏玩家的福音。市面上虽说有众多云电脑可供我们选择,但小编试用过这么多后还是最推荐ToDesk的云电脑。覆盖的系统够全面,3060和4070配置足够玩游戏,而且......
  • FIT2107 - Software Quality and Testing
    FIT2107-SoftwareQualityandTestingASSIGNMENT2[40%]WhiteboxtestingandcodeanalysisOverviewForthisassignment,yourtaskistodesignanddocumentappropriatetestsforasoftwaresystemusingwhiteboxtechniques,buildaCI/CDpipelinetor......
  • 【题解】Solution Set - NOIP2024集训Day50 图的连通性相关
    【题解】SolutionSet-NOIP2024集训Day50图的连通性相关https://www.becoder.com.cn/contest/5618「JSOI2012」越狱老虎桥简述题意:题目大意:给定一张图,A先添加\(1\)条边,B再删去一条边使得图不连通,A要最大化删除边的权值,B要最小化删除边的权值,问最终的权值是多少。......
  • 洛谷 P2071 座位安排题解
    因为一个人坐一个座位很像二分图,题意转化为二分图最大匹配。把人放在左部,把座位放在右部,一排座位占右部的两个点。假设人想要坐在\(x\)排,那么建图的时候就可以将这个人连向\(2x\)和\(2x+1\)。这样一排就对应着两个人了。由于\(n\le4000\),直接由朴素的\(O(nm)\)的匈牙利......