给出一正整数 n n n,找到 { 1 , 2 , 3 , . . . , n } \{1,2,3,...,n\} {1,2,3,...,n}的两个子集合 A , B A,B A,B,使:
(1) ∣ A ∣ = ∣ B ∣ = m , A ∩ B = ∅ |A|=|B|=m,A∩B=∅ ∣A∣=∣B∣=m,A∩B=∅
(2)令 A = { a 1 , a 2 . . . a m } , B = { b 1 , b 2 , . . . b m } A=\{a_1,a_2...a_m\},B=\{b_1,b_2,...b_m\} A={a1,a2...am},B={b1,b2,...bm},存在两种排列方式 { p 1 , p 2 , . . . p m } , { q 1 , q 2 , . . . , q m } \{p_1,p_2,...p_m\},\{q_1,q_2,...,q_m\} {p1,p2,...pm},{q1,q2,...,qm},
使得对于 ∀ i ∈ [ 1 , m ] , g c d ( a p i , b q i ) > 1 \forall i∈[1,m],gcd(a_{p_i},b_{q_i})>1 ∀i∈[1,m],gcd(api,bqi)>1。
2
4
10
1
2 4
4
3 9
5 10
8 2
4 6
其实是个CF原题 Codeforces450E
总体思路是先找到所有小于 n n n的质因子 p i p_i pi,然后构造" p i p_i pi k ∗ p i k*p_i k∗pi"之类的倍数形式。
对于某一个质因子 p i p_i pi,我们最多可以找到 ⌊ n p i ⌋ \lfloor\frac{n}{p_i}\rfloor ⌊pin⌋个它的倍数(其实还要在这个基础上去减掉已经被计算过的数字,记为 c o u n t count count)。
如果个数 c o u n t count count为偶数,那就直接两两配对完事。
如果是奇数,在配对完之后我们剩下一个数,为了保证能配对的尽量多,我们可以去找一个偶数,
把它放在另一个容器里,在其他所有质因子完成配对后,对这个容器里的那些偶数随意地两两排列即可(gcd起码也有2)。
质数筛的代码出现了一些问题,还需要调整…
#include
using namespace std;
const long long MAXN=2e5;
int prime[MAXN];//素数数组
bool is_prime[MAXN+10];//is_pri[i]表示i是素数
bool vis[MAXN+10];//是否被访问
int rest[MAXN+10];//当出现奇数个的时候被拎出来的偶数
int pow_prime[MAXN+10];//n范围内p的倍数、且未被访问过
int answer[MAXN+10][3];//存答案
//返回n以内素数的个数
long long sieve(long long n)
{
for(int i=0; i<=n; i++)
{
is_prime[i]=true;
vis[i]=false;
}
is_prime[0]=is_prime[1]=false;//首先标记0和1不是素数
is_prime[2]=true;//标记2是素数
for(int i=2; i<=sqrt(n); i++)
{
if (is_prime[i]) //如果i是素数
{
for(int j=i*i; j<=n; j+=i)//所有i的倍数都不是素数
is_prime[j]=false;
}
}
long long p=0;
for(int i=2; i<=MAXN; i++)
{
if (is_prime[i])
{
prime[++p]=i;
}
}
return p;
}
void solve1()
{
long long sum_prime=sieve(MAXN);//质数个数
int t;
scanf("%d", &t);
while(t--)
{
memset(vis, false, sizeof(vis));//每次重置vis的访问情况
long long n, m=0;
scanf("%lld", &n);
long long maxP=1;
long long sum_rest=0;//匹配剩下的个数的总数
for(int i=sum_prime; i>=1; i--)//找出最大的质数
{
if (prime[i]*2<=n)
{
maxP=i;
break;
}
}
for(int i=maxP; i>=1; i--)
{
long long p=prime[i];//质因子
long long sum_vis=0;//计算未被访问的p的倍数的个数
for(int j=p; j<=n; j+=p)
{
if (!vis[j])
{
pow_prime[++sum_vis]=j;//存储p的倍数、且未被使用的
}
}
if (sum_vis==1)//一个的时候无法配对
continue;
if (sum_vis%2==1)//奇数
{
int pos=1;
for(int j=1; j<=sum_vis; j++)
{
if (pow_prime[j]%2==0)//找到第一个是偶数的(貌似可以直接2*p?)
{
pos=j;
break;
}
}
rest[++sum_rest]=pow_prime[pos];//加入rest
vis[pow_prime[pos]]=true;//更新访问标记
for(int j=1; j+1<=sum_vis; j+=2)//开始配对
{
m++;
if (j==pos)
j++;
answer[m][1]=pow_prime[j];
if (j+1==pos)
j++;
answer[m][2]=pow_prime[j+1];
vis[pow_prime[j]]=true;//更新访问标记
vis[pow_prime[j+1]]=true;//更新访问标记
}
}
else//偶数任意配对
{
for(int j=1; j+1<=sum_vis; j+=2)//开始配对
{
m++;
answer[m][1]=pow_prime[j];
answer[m][2]=pow_prime[j+1];
vis[pow_prime[j]]=true;//更新访问标记
vis[pow_prime[j+1]]=true;//更新访问标记
}
}
}
if (sum_rest>=2)//rest内的配对
{
for(int i=1; i+1<=sum_rest; i+=2)
{
m++;
answer[m][1]=rest[i];
answer[m][2]=rest[i+1];
}
}
printf("%lld\n", m);
for(int i=1; i<=m; i++)
{
printf("%lld %lld\n", answer[i][1], answer[i][2]);
}
}
}
int main()
{
// ios_base::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
#ifdef ACM_LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
long long test_index_for_debug=1;
char acm_local_for_debug;
while(cin>>acm_local_for_debug)
{
cin.putback(acm_local_for_debug);
if (test_index_for_debug>100)
{
throw runtime_error("Check the stdin!!!");
}
auto start_clock_for_debug=clock();
solve1();
auto end_clock_for_debug=clock();
cout<<"\nTest "<<test_index_for_debug<<" successful"<<endl;
cerr<<"Test "<<test_index_for_debug++<<" Run Time: "
<<double(end_clock_for_debug-start_clock_for_debug)/CLOCKS_PER_SEC<<"s"<<endl;
cout<<"--------------------------------------------------"<<endl;
}
#else
solve1();
#endif
return 0;
}
赛后发现质数筛和一些细节错误导致通过率0.00%
这破题真就百度之星呗
DrGilbert 2020.7.20