NOIP模拟赛2019.8.22题解

T1 指引(guide)

NOIP模拟赛2019.8.22题解_第1张图片
NOIP模拟赛2019.8.22题解_第2张图片

100pts

将旅行者和城市的坐标都存储在一个结构体数组中。按照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;
} 

T2 碎片(fragment)

NOIP模拟赛2019.8.22题解_第3张图片
NOIP模拟赛2019.8.22题解_第4张图片

100pts

对于每一列,我们交换任意两列,不会改变这两列中数的相对,交换任意两行,只会改变每列中两个元素的位置。对于每一行也一样。所以任意的操作不会改变任何和行和列的数的种类。

每个矩阵只能有一行和一列是无法配对的(显然是在行和列是奇数的情况下),其余行列必须有和它完全相同(有完全相同的元素)的列或行进行两两匹配。

于是先初始化,枚举每两行/列,标记能两两配对的情况。

然后把序列分成两半,暴力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;
} 

T3 寻梦(fantasy)

NOIP模拟赛2019.8.22题解_第5张图片
NOIP模拟赛2019.8.22题解_第6张图片

暴力:

A 是一个 n 位的没有相同元素的排列,若 A 中出现两个相同的数,那么必然会有2个点一直处在同一位置,不能到达各自的家乡。

70pts

在一个排列转换中,将 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判断是否存在非负整数解即可。

100pts

说实话这个思路看了一个多小时还是似懂非懂,贴一下大佬的讲解叭。

当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 内的质数。

你可能感兴趣的:(模拟赛)