CSDN 第 7 期题解(只有 T4 代码)

被迫营业

没代码,早删了。

因为 T2 暴力 T 了浪费了好几分钟,生气。

T1

题目大意

给定一个长度为 n n n 的序列,你需要将所有奇数放到前面,偶数放到后面,如果两个数奇偶性相同,原位置小的在前面。

数据范围

n ⩽ 5 × 1 0 5 n\leqslant 5\times 10^5 n5×105(开多少都无所谓反正我不记得了), a i a_i ai 不重要。

题解

太简单了,可惜 csdn 没有把题面描述的太好,看了之后完全不知道还有位置的关键字需要排,希望 csdn 下次能加强题目描述的漏洞,争取没有瑕疵。

解法一:你可以自己定义一个 cmp 函数,如果发现 x , y x,y x,y 奇偶性不相同,则哪个奇数返回哪个,奇偶性相同自己看一下位置就完事了,时间复杂度 O ( n log ⁡ n ) \mathcal{O}(n\log n) O(nlogn),好写好操作。

解法二:按顺序遍历数组先把所有奇数提出来再把所有偶数提出来,时间复杂度 O ( n ) \mathcal{O}(n) O(n)

T2

题目大意

给定字符串 S S S,求出其最长回文子串的长度。

数据范围

∣ S ∣ ⩽ 10000 \lvert S\rvert\leqslant 10000 S10000

题解

太简单了,我不建议出这种考过太多次的题目。

解法一(建议初学者学习):枚举回文子串的中心在哪里,然后从这个中心开始往两边遍历,即可求得最长回文子串的长度,时间复杂度 O ( n 2 ) \mathcal{O}(n^2) O(n2),已经可以过了。

解法二:经典 manacher 算法,模板见:【模板】manacher 算法,时间复杂度 O ( n ) \mathcal{O}(n) O(n),可以跑到 n = 1 0 7 n=10^7 n=107。点开题解可以看 manacher 的核心思路,不细说了。

T3

题目大意

有两个 01 \tt 01 01 a , b a,b a,b。你可以在 a a a 中进行一次操作:选定一对 ( i , j ) (i,j) (i,j) 满足 i ≠ j , 1 ⩽ i , j ⩽ n i\neq j,1\leqslant i,j\leqslant n i=j,1i,jn,交换 a i , a j a_i,a_j ai,aj。问有多少本质不同的 ( i , j ) (i,j) (i,j) 可以使 a    or    b a\;\text{or}\;b aorb 的值发生变化。

数据范围

n ⩽ 5 × 1 0 5 n\leqslant 5\times 10^5 n5×105(反正开多少不重要)。

题解

太简单了,纯纯一眼题。

分类讨论一下:

如果我在 a a a 串里面找到一个 1 1 1,并且 b b b 串中这个位置也是 1 1 1,那么我能产生贡献当且仅当我把 a a a 串的 1 1 1 挪动到了一个 b b b 串中不为 1 1 1 的位置。这种情况的总方案数应当是 a , b a,b a,b 中组成 ( 0 , 0 ) (0,0) (0,0) 的个数。

如果我在 a a a 串里面找到一个 1 1 1,并且 b b b 串中这个位置是 0 0 0,则只要我这个 1 1 1 不与其他 1 1 1 交换就行,总方案数是 ( 0 , x ) (0,x) (0,x) 的方案数。

随便求一求就好了,总时间复杂度 O ( n ) \mathcal{O}(n) O(n)

T4

题目大意

有一个由 1 ∼ n 1\sim n 1n 中所有整数构成的集合 S S S,现在给了你 m m m 个整数,你需要除去 S S S 中所有 m m m 的倍数,问还剩下多少个数没被除去。

数据范围

n ⩽ 1 0 9 , m ⩽ 10 n\leqslant 10^9,m\leqslant 10 n109,m10

题解

太简单了,一眼就知道要状压。

观察到 m ⩽ 10 m\leqslant 10 m10,所以大概率是搜索题/状压题,再考虑要除去倍数。一般除去倍数我们使用容斥原理,即先除去 a a a 的倍数,再除去 b b b 的倍数,最后加回 lcm ⁡ ( a , b ) \operatorname{lcm}(a,b) lcm(a,b) 的倍数。

所以解法是定义 i i i 为这十个数的状态, 1 1 1 表示我要选这个数,否则我不选。考虑这 i i i 个数进行 lcm ⁡ \operatorname{lcm} lcm 后可以除去的数。根据容斥,我们一般是加减交替进行的,在本题中,数量为奇数则减去,偶数则加回来,所以记状态 i i i 1 1 1 的个数,考虑我要减还是加, lcm ⁡ \operatorname{lcm} lcm 随便求,最常见做法是 a b gcd ⁡ ( a , b ) \dfrac{ab}{\gcd(a,b)} gcd(a,b)ab,这里 gcd ⁡ ( a , b ) \gcd(a,b) gcd(a,b) 你可以用自带库,也可以自己用欧几里得算法做。时间复杂度 O ( m 2 m ) \mathcal{O}(m2^m) O(m2m),完全能过。没算 gcd ⁡ \operatorname{gcd} gcd 的复杂度,反正无所谓,就是一个 log ⁡ \log log 的事情。

顺带一提你完全没必要开一个 map 去记录某个数有没有被除去,显然这是多余的,你除去的时候直接减去 n lcm ⁡ ( a i ) \dfrac{n}{\operatorname{lcm}(a_i)} lcm(ai)n 或者加上就完事了,本题不关心有哪些数剩下来了,他只关心数量,所以 O ( n m ) \mathcal{O}(nm) O(nm) 的做法我认为还是比较愚蠢的(使用 unordered_map 可以把 map O ( log ⁡ n ) \mathcal{O}(\log n) O(logn) 时间复杂度除去,但要知道不管啥都开不下 1 0 9 10^9 109 的数组)。

解法一:状压循环同上。
解法二:搜索也能做,但考虑到递归常数应该比状压大,我不建议。

数据分治的人差不多得了别在那里误导人/cy

更新一下,那些明明不会 T4 还要写个 O ( n m ) \mathcal{O}(nm) O(nm) 暴力装作这是正解的人都是什么成分啊?你觉得你过得了?还有一笔带过这是容斥定理的讲的完全不对/lh

为了防止更多萌新被那些 O ( n m ) \mathcal{O}(nm) O(nm) 误导,我赛后复盘了一下 T4 代码,这个代码也许会出现一点小问题,但时间复杂度是正确的:

#include<bits/stdc++.h>
using namespace std;
const int N=15;
int n,m,a[N];
int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
int lcm(int x,int y){return x/gcd(x,y)*y;}//防止溢出,先除,显然正确
int main(){
    scanf("%d %d",&n,&m);
    int ans=n;
    for(register int i=1;i<=m;i++) scanf("%d",&a[i]);
    for(register int i=1;i<(1<<m);i++){
        //从1开始枚举是因为我要保证至少要选一个数
        //枚举m中每个数的状态,如果第i个二进制位为1表示我现在要将第i-1个数计入lcm
        int x=1,cnt=0;
        for(register int j=1;j<=m;j++){
            if(i&(1<<(j-1))){//检查这一位是不是1
                cnt++;
                x=lcm(x,a[j]);
            }
        }
        if(cnt&1) ans-=n/x;//根据容斥定理判断现在要加上贡献还是减去
        else ans+=n/x;
    }
    printf("%d",ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值