Harder Gcd Problem 2020牛客多校第四场(质数筛+构造)

原题题面

给出一正整数 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,AB=
(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 kpi"之类的倍数形式。
对于某一个质因子 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)。

AC代码(25ms)

质数筛的代码出现了一些问题,还需要调整…

#include <bits/stdc++.h>
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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用正则表达式来匹配并替换<>中间的内容,Java中可以使用String类的replaceAll()方法来实现。具体代码如下: ```java String email = "Try harder<2825741341@qq.com>"; String pattern = "<(.*?)>"; // 匹配<>中间的内容 String replacement = ""; // 替换为空字符串 String result = email.replaceAll(pattern, replacement); System.out.println(result); // 输出:Try harder ``` 其中,"(.*?)"表示非贪婪模式匹配,即尽可能少地匹配符合条件的字符。 ### 回答2: 在Java中,我们可以使用正则表达式来提取字符串中的特定内容。要将"<2825741341@qq.com>"中间的内容提取出来,可以按照以下步骤进行操作: 首先,导入java.util.regex包,使用Pattern类和Matcher类来进行正则匹配。代码如下: ```java import java.util.regex.Matcher; import java.util.regex.Pattern; ``` 然后,使用正则表达式将"< >"中间的内容提取出来。代码如下: ```java String email = "Try harder<2825741341@qq.com>"; String regex = "<(.*?)>"; // 使用非贪婪模式,匹配< >之间的内容 Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(email); if (matcher.find()) { String extractedContent = matcher.group(1); System.out.println(extractedContent); } ``` 运行以上代码,输出结果为: ``` 2825741341@qq.com ``` 这样,我们就成功提取出了"<2825741341@qq.com>"中间的内容"2825741341@qq.com"。 ### 回答3: 你可以使用Java中的字符串处理方法来提取<>中间的内容。以下是一个简单的实现示例: ```java public class ExtractEmailContent { public static void main(String[] args) { String email = "Try harder<2825741341@qq.com>"; int start = email.indexOf("<"); int end = email.indexOf(">"); if (start != -1 && end != -1) { String content = email.substring(start + 1, end); System.out.println(content); } else { System.out.println("未找到<>中间的内容"); } } } ``` 这段代码通过使用`indexOf()`方法找到字符串中的`<`和`>`的位置。然后,使用`substring()`方法提取这两个符号之间的内容,并将提取到的内容打印出来。 在本例中,将打印出`2825741341@qq.com`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值