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 n≤2∗105,n−1≤m≤5∗105)。
解法:
首先,如果这样的点对存在,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 N∗M的矩阵使同一行和同一列上无相同数字。最大化 N ∗ M N*M N∗M的值 ( n ≤ 4 ∗ 1 0 5 ) (n\le4*10^5) (n≤4∗105)。
解法:
设矩阵大小为 N ∗ M ( N ≤ M ) N*M(N\le M) N∗M(N≤M),则当矩阵中各数字出现次数的最大值不超过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时,填写次序如下图。
1 | 17 | 13 | 9 | 5 |
---|---|---|---|---|
6 | 2 | 18 | 14 | 10 |
11 | 7 | 3 | 19 | 15 |
16 | 12 | 8 | 4 | 20 |
注意:相同的数字必须被连续的填入矩阵。由于 N ∗ M N*M N∗M矩阵中各数字最多出现N次,同行同列上不可能存在相同的数字。
根据以上分析,先利用map预处理出出现次数为 i ( 1 ≤ i ≤ n ) i(1\le i\le n ) i(1≤i≤n)的数并用vector存放,进而利用前缀和的思想可求出至少出现 i i i次的数的个数sum[i]。遍历矩阵行数: i = 1 → n i=1\rightarrow\sqrt n i=1→n,变量cnt记录在行数为 i i i的矩阵中最多能填多少个数字,于是行数为 i i i的矩阵最大列数为 j = c n t / i j=cnt/i j=cnt/i,用 i ∗ j i*j i∗j更新答案。确定矩阵大小后按上述方法构造矩阵即可。
复杂度: 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("");
}
}