近期所学

最长上升子序列nlogn的做法

思路:维护一个递增数组,长度相同的LIS只存终点的最小值

1
2
3
4
5
6
7
8
9
10
11
12
int lengthOfLIS(vector<int>& nums){
int n=nums.size();
vector<int> vec;
for(int i=0;i<n;i++){
int p=lower_bound(vec.begin(),vec.end(),nums[i])-vec.begin();
if(p==vec.size())
vec.push_back(nums[i]);
else
vec[p]=nums[i];
}
return vec.size();
}

同类问题:给出一个由字符 ‘0’ 和 ‘1’ 组成的字符串 S,我们可以将任何 ‘0’ 翻转为 ‘1’ 或者将 ‘1’ 翻转为 ‘0’,返回使 S 单调递增的最小翻转次数

最近接触了很多可以转换成lis问题的dp题,感觉很神奇,就比如牛客的一道叫免费馅饼的题,可以通过旋转坐标轴,求新的x,y变量的lis(具体在cometoj)

容斥定理

规律奇加偶减

求[a,b]中与n互素的个数(co-prime)

思路:1~100中与30不互素的有是2,3,5倍数的-即是2又是3倍数的-即是2又是5倍数的-即是3又是5倍数的<3,5是两个数即为偶数>+即是2又是3又是5倍数的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
bool bprime[N];
LL prime[N],cnt, factor[N],num;
void isprime() {
cnt=0;
memset(bprime,false,sizeof(bprime));
for(LL i=2; i<N; i++) {
if(!bprime[i]) {
prime[cnt++]=i;
for(LL j=i*i; j<N; j+=i)
bprime[i]=true;
}
}
}
void getFactor(int n){
num=0;
for(LL i=0; prime[i]*prime[i]<=n&&i<cnt; i++) {
if(n%prime[i]==0) {
factor[num++]=prime[i];
while(n%prime[i]==0)
n/=prime[i];
}
}
if(n!=1)
factor[num++]=n;
}  
LL calculate(LL m,LL num) {
LL res=0;
for(LL i=1; i<(1<<num); i++) {
LL sum=0;
LL temp=1;
for(LL j=0; j<num; j++) {
if(i&(1<<j)) {
sum++;
temp*=factor[j];
}
}
if(sum%2)
res+=m/temp;
else
res-=m/temp;
}
return res;
}
int main() {
isprime();
LL a,b,n;
scanf("%lld%lld%lld",&a,&b,&n);
getFactor(n)
LL res=(b-(a-1)-calculate(b,num))+calculate(a-1,num);
printf("%lld\n",res);
return 0;
}

求[1,n]中能/不能被m个数整除的个数

思路:sum=从 m 中选 1 个数得到的倍数的个数-从 m 中选 2 个数得到的倍数的个数 + 从 m 中选 3 个数得到的倍数的个数 - 从 m 中选 4 个数得到的倍数的个数……
能被整除的个数就是 sum,不能被整除的个数就是 n-sum

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
LL GCD(LL a,LL b){
return !b?a:GCD(b,a%b);
}
LL LCM(LL a,LL b){
return a/GCD(a,b)*b;
}
LL a[N];
int main(){
LL n;
int m;
scanf("%lld%d",&n,&m);
for(int i=0;i<m;i++)
scanf("%lld",&a[i]);

LL sum=0;
for(int i=0;i<(1<<m);i++){
LL lcm=1;
LL cnt=0;
for(int j=0;j<m;j++){
if(i&(1<<j)){
lcm=LCM(lcm,a[j]);
cnt++;
}
}
if(cnt!=0){
if(cnt&1)
sum+=n/lcm;
else
sum-=n/lcm;
}
}
printf(%lld "%lld\n",sum,n-sum);
return 0;
}

给出一串数,要求出删去最少个数字,使这串数的gcd增大

思路:只要gcd变大就行,至于删多少数字,先将所有数的公共gcd除去,然后分解质因数找最大数量(cnt)的因子,每轮对n-cnt取最小或者对cnt取最大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 15000000;
int vis[maxn],a[maxn],flag[maxn];
int gcd(int a,int b)
{
return b == 0 ? a : gcd(b,a % b);
}
int main()
{
memset(flag,0,sizeof(flag));
memset(vis,0,sizeof(vis));
int n;
int gc = 0;
scanf("%d",&n);
for(int i = 1;i <= n;i++)
{
scanf("%d",&a[i]);
vis[a[i]]++;
if(!gc)
gc = a[i];
else
gc = gcd(gc,a[i]);
}
int ans = 0;
for(int i = gc + 1;i <= maxn;i++)
{
if(!flag[i])
{
int cnt = 0;
for(int j = i; j <= maxn; j += i)
{
cnt += vis[j];
flag[j] = 1;
}
ans = max(ans,cnt);
}
}
if(ans > 0) printf("%d\n",n - ans);
else printf("-1\n");
return 0;
}

一些关于质数和整除的小规律

任何一个合数n必定包含一个不超过sqrt(n)的质因子

区间筛是在l~r区间内筛prime[sqrt(r)]以内]的倍数

N!中的每个质因子都不会超过N

gcd(a,b)=gcd(a,b-a)=gcd(a,b mod a)

c|a,c|b,则c|sa+tb