将旅行者和城市的坐标都存储在一个结构体数组中。按照x从小到大排序。
扫描到旅者时,把y值放入set中;扫描到城市时,找set中最大的小于等于当前城市y的值,找到了ans++,并且删除这个值。(因为一开始x从小到大排序保证了set中保持的旅者的x都小于当前城市)。
查找用lower_bound/upper_bound,如果直接扫描会T一个点。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200005;
int n,ans=0,num;
set<int>s;
struct st{
ll x,y;int id;
}a[N],b[N];
ll read(){
ll sum=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return sum*f;
}
void print(ll x)
{
if(x<0)x=-x,putchar('-');
if(x>9)print(x/10);
putchar(x%10+'0');
}
bool comp(st a,st b){
return a.x<b.x;
}
int main(){
// freopen("guide.in","r",stdin);
// freopen("guide.out","w",stdout);
num=read();n=read();
for(int i=1;i<=n;i++)
{
a[i].x=read();a[i].y=read();a[i].id=1;
}
for(int i=n+1;i<=2*n;i++)
{a[i].x=read();a[i].y=read();
}
sort(a+1,a+2*n+1,comp);
int x,y;
for(int i=1;i<=2*n;i++)
{
if(a[i].id){s.insert(a[i].y);}
else{
set<int>::iterator it=s.upper_bound(a[i].y);
if(it==s.begin())continue;
it--;ans++;
s.erase(it);
}
}
print(ans);
return 0;
}
对于每一列,我们交换任意两列,不会改变这两列中数的相对,交换任意两行,只会改变每列中两个元素的位置。对于每一行也一样。所以任意的操作不会改变任何和行和列的数的种类。
每个矩阵只能有一行和一列是无法配对的(显然是在行和列是奇数的情况下),其余行列必须有和它完全相同(有完全相同的元素)的列或行进行两两匹配。
于是先初始化,枚举每两行/列,标记能两两配对的情况。
然后把序列分成两半,暴力dfs。行/列是奇数的时候枚举哪一行/列在最中间单独存在。否则对于左右两边枚举能够两两配对的行,然后枚举能两两配对的列。
每次枚举完check一下,看是否是中心对称。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char a[13][13],l[13],r[13];
int n,m,num,t,vis1[13],vis2[13],vn[13][13],vm[13][13],dx[13],dy[13],flag;
ll read(){
ll sum=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return sum*f;
}
void print(ll x)
{
if(x<0)x=-x,putchar('-');
if(x>9)print(x/10);
putchar(x%10+'0');
}
void check(){
for(int i=1;i<=(1+n)/2;i++)
for(int j=1;j<=(1+m)/2;j++)
{
if(a[dx[i]][dy[j]]!=a[dx[n+1-i]][dy[m+1-j]])return;
}
flag=1;return;
}
void dfsm(int now,int q)
{
if(flag)return;
if(now==0)
{
check();return;
}
if(q){
for(int i=1;i<=m;i++)
{
vis2[i]=1;dy[now]=i;
dfsm(now-1,0);
vis2[i]=0;
}
}
else{
for(int i=1;i<=m;i++)
if(!vis2[i])
for(int j=i+1;j<=m;j++)
if(!vis2[j]&&vm[i][j]){
vis2[i]=vis2[j]=1;
dy[now]=j; dy[m+1-now]=j;
dfsm(now-1,0);
vis2[j]=vis2[j]=0;
}
}
}
void dfsn(int now,int q){
if(flag)return;
if(now==0)
{
dfsm((1+m)/2,m&1);return;
}
if(q){
for(int i=1;i<=n;i++)
{
vis1[i]=1;dx[now]=i;
dfsn(now-1,0);
vis1[i]=0;
}
}
else{
for(int i=1;i<=n;i++)
if(!vis1[i])
for(int j=i+1;j<=n;j++)
if(!vis1[j]&&vn[i][j]){
vis1[i]=vis1[j]=1;
dx[now]=i;dx[n+1-now]=j;
dfsn(now-1,0);
vis1[i]=vis1[j]=0;
}
}
}
void init(){
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
for(int k=1;k<=m;k++)
{
l[k]=a[i][k];r[k]=a[j][k];
}
sort(l+1,l+m+1);
sort(r+1,r+m+1);
vn[i][j]=1;
for(int k=1;k<=m;k++)
if(l[k]!=r[k]){vn[i][j]=0;break;}
}
for(int i=1;i<=m;i++)
for(int j=i+1;j<=m;j++)
{
for(int k=1;k<=n;k++)
{
l[k]=a[k][i];r[k]=a[k][j];
}
sort(l+1,l+n+1);
sort(r+1,r+n+1);
vm[i][j]=1;
for(int k=1;k<=n;k++)
if(l[k]!=r[k]){vm[i][j]=0;break;}
}
}
int main(){
// freopen("fragment.in","r",stdin);
// freopen("fragment.out","w",stdout);
num=read();t=read();
while(t--)
{
n=read();m=read();flag=0;
for(int i=1;i<=n;i++)
scanf("%s",a[i]+1);
init();
dfsn((1+n)/2,n&1);
if(flag)printf("YES\n");
else printf("NO\n");
}
return 0;
}
A 是一个 n 位的没有相同元素的排列,若 A 中出现两个相同的数,那么必然会有2个点一直处在同一位置,不能到达各自的家乡。
在一个排列转换中,将 i 向 Ai 连边,其实就可以看成是若干个环,求解是否存在一些大于 1 的整
数 ci,使得 lcm(ci)|k 且∑ci=n。
转化为对于 ci|k,ci>1,是否存在非负整数 ti 使得∑(ti * ci)=n。对于一个不是质数的数 ci,可以将其拆成若干 k
的质因数的乘积,那么题目要求可以进一步地看作是对于 k 的质因数 ci,判断存在非负整数
ti 使得∑(ti * ci)=n。
特判:若k的质因数分解最多两个数,用exgcd判断是否存在非负整数解即可。
说实话这个思路看了一个多小时还是似懂非懂,贴一下大佬的讲解叭。
当k的质因数大于等于3时,除了最小质因数 x 以外的质因数构造出 T,使得 T≡n(mod x),那么在 T 小于等于 n 的基础上加上一些 x 即可使和为 n。
如何判断是否存在这样的 T?
在模意义下,将 t 向 t+ci 连边,边权为 ci,跑单源最短路,求出的 0~n%x 的最短路的长度即为上文需要的 T。
当 k 达到 10^15,那么最小质因数可以达到 3 *10^7,跑不了最短路。
考虑将质因数个数为 2 已特判掉,那么最小质因数最大就在三次根号下 10^15,
即为 105。同时,因为质因数分解过慢,要预处理√1015 内的质数。