Codeforces Round #606 Div2

1259E Two Fairs(搜索)

题意:

​ 给一个无向连通图以及图上不同的两点a、b,求出满足条件的不同点对(x,y)个数,其中点x到点y的任意一条路径都会经过a和b两点,(x,y)与(y,x)视作相同( n ≤ 2 ∗ 1 0 5 , n − 1 ≤ m ≤ 5 ∗ 1 0 5 n\le2*10^5,n-1\le m\le5*10^5 n2105,n1m5105)。

解法:

​ 首先,如果这样的点对存在,a和b必定是图上的两个割点。如果(x,y)满足条件,那么删掉a或者b任意一点后,x与y不再连通。由此,图上的点可以被分为三类:1. 删去点b后,与a连通的点;2. 删去点a后,与b连通的点;3. 同时满足1和2的点。那么设任意第一类点为x,任意第二类点为y,则点对(x,y)满足条件,而其他情况均不满足,因此答案为第一类点个数*第二类点个数。

​ 于是分别以点a和点b为起点开始搜索分别与a和b连通的点,利用set维护,最后遍历1-n计算两类点的数量。

​ 复杂度: O ( n ) O(n) O(n)

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
vector<int>e[maxn];
set<int>s[2];
int a[2],flag[2][maxn];
void link(int u,int v)
{	e[u].push_back(v),e[v].push_back(u); }
void dfs(int u,int tag)
{
	flag[tag][u]=true;
	if(u==a[tag^1]) return;
	s[tag].insert(u);
	for(int v:e[u])
		if(!flag[tag][v]) dfs(v,tag);
}
int main()
{
	int T,n,m,u,v;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d%d",&n,&m,&a[0],&a[1]);
		for(int i=1;i<=n;i++) e[i].clear();
		s[0].clear(),s[1].clear();
		for(int i=1;i<=m;i++)
			scanf("%d%d",&u,&v),link(u,v);
		dfs(a[0],0),dfs(a[1],1);
		int cnt[2]={0};
		for(int i=1;i<=n;i++) 
			if(i!=a[0]&&i!=a[1]&&(s[0].count(i)^s[1].count(i)))
				cnt[0]+=s[0].count(i),cnt[1]+=s[1].count(i);
		printf("%lld\n",1LL*cnt[0]*cnt[1]);
	}
}

1259F Beautiful Rectangle(构造 数学)

题意:

​ 给n个整数,构造一个 N ∗ M N*M NM的矩阵使同一行和同一列上无相同数字。最大化 N ∗ M N*M NM的值 ( n ≤ 4 ∗ 1 0 5 ) (n\le4*10^5) (n4105)

解法:

​ 设矩阵大小为 N ∗ M ( N ≤ M ) N*M(N\le M) NM(NM),则当矩阵中各数字出现次数的最大值不超过N时,可构造一个矩阵满足同行同列上无相同数字。构造方法如下,从 ( 0 , 0 ) (0,0) (0,0)开始填数,当前填到 ( x , y ) (x,y) (x,y),则下一步填 ( ( x + 1 ) % N , ( y + 1 ) % M ) ((x+1)\% N,(y+1)\%M) ((x+1)%N,(y+1)%M);若该处已被填过,则使 x = ( x + 1 ) % N x=(x+1)\%N x=(x+1)%N。例:当N=4,M=5时,填写次序如下图。

1171395
62181410
11731915
16128420

​ 注意:相同的数字必须被连续的填入矩阵。由于 N ∗ M N*M NM矩阵中各数字最多出现N次,同行同列上不可能存在相同的数字。

​ 根据以上分析,先利用map预处理出出现次数为 i ( 1 ≤ i ≤ n ) i(1\le i\le n ) i(1in)的数并用vector存放,进而利用前缀和的思想可求出至少出现 i i i的数的个数sum[i]。遍历矩阵行数 i = 1 → n i=1\rightarrow\sqrt n i=1n ,变量cnt记录在行数为 i i i的矩阵中最多能填多少个数字,于是行数为 i i i的矩阵最大列数 j = c n t / i j=cnt/i j=cnt/i,用 i ∗ j i*j ij更新答案。确定矩阵大小后按上述方法构造矩阵即可。

​ 复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

#include<bits/stdc++.h>
using namespace std;
const int maxn=4e5+5;
vector<int>num[maxn];
map<int,int>mp;
int sum[maxn];
int main()
{
	int n,a;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a),mp[a]++;
	for(auto i:mp)
		num[i.second].push_back(i.first);
	sum[n]=num[n].size();
	for(int i=n-1;i;i--)
		sum[i]=sum[i+1]+num[i].size();
	int siz=0,p=0,q=0,cnt=0;
	for(int i=1;i*i<=n;i++)
	{
		cnt+=sum[i];
		int j=cnt/i;
		if(i<=j&&i*j>siz) siz=i*j,p=i,q=j;
	}
	printf("%d\n%d %d\n",siz,p,q);
	vector<vector<int> >ans(p,vector<int>(q));
	int x=0,y=0;
	for(int i=n;i;i--)
		for(int val:num[i])
			for(int j=1;j<=min(i,p);j++)
			{
				if(ans[x][y]) x=(x+1)%p;
				if(!ans[x][y]) ans[x][y]=val;
				x=(x+1)%p,y=(y+1)%q;
			}
	for(int i=0;i<p;i++)
	{
		for(int j=0;j<q;j++)
			printf("%d ",ans[i][j]);
		puts("");
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值