1.来个小定理(上次DP的青蛙过河用过)
事实上,假如他们的gcd!=1,那么P,q都可以表示成gcd的倍数,因此假如一个数不是gcd的倍数就不可以表示,若互质由裴蜀定理大于一定时一定可以表示出。
事实上为(p-1)(q-1)-1.(p,q互质)
2.
模拟+单链表式并查集:
p[i]表示单链表的下一个节点,i所在树的根节点为从i开始向右找第一个没有被用的位置。
一开始都是自环,假如后面2被用过,那么2连3,假如1用了,1连2,这样子通过路径压缩就可以高效的实现。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1100010;
int p[N];
int find(int x){
if(p[x]==x) return x;
return p[x]=find(p[x]);
}
int main(){
int n;
cin>>n;
for(int i=1;i<N;i++) p[i]=i;
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
x=find(x);
printf("%d ",x);
p[x]=x+1;
}
}
3.组合数问题求最值-背包:
我们令f[i][j][k]表示从前i个数选j个,总和余数为k的选法集合的max
易得状态转移方程:
f[i][j][k]=max(f[i-1][j][k],f[i-1][j-1][((k-ai)%K+K)%K]),但是复杂度太大了。
同时,我们可以得到一个结论,对于余数相同的,我们只要保证存最大值前3就可以了,这样复杂度就可以了
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m,f[4][N];
vector<int> a[N];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
a[x%m].push_back(x);
}
memset(f,-0x3f,sizeof(f));
f[0][0]=0;
for(int i=0;i<m;i++){
sort(a[i].begin(),a[i].end());
reverse(a[i].begin(),a[i].end());
for(int u=0;u<3&&u<a[i].size();u++){
int x=a[i][u];
for(int j=3;j>=1;j--){
for(int k=0;k<m;k++){
f[j][k]=max(f[j][k],f[j-1][(k-x%m+m)%m]+x);
}
}
}
}
cout<<f[3][0];
}
4.数学
恶心到了,直接放图:
下面是矩阵快速幂以及龟速乘的代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL p;
LL qmul(LL a,LL b){
LL res=0;
while(b){
if(b&1) res=(res+a)%p;
a=(a+a)%p;
b>=1;
}
return res;
}
LL t[2][2];
void mul(LL c[][2],LL a[][2],LL b[][2]){
memset(t,0,sizeof(t));
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
for(int k=0;k<2;k++){
t[i][j]=(t[i][j]+qmul(a[i][k],b[k][j]))%p;
}
}
}
memcpy(c,t,sizeof(t));
}
LL F(LL n){
if(!n) return 0;
LL f[2][2]={1,1};
LL a[2][2]={{0,1},{1,1}};
for(LL k=n-1;k;k>=1){
if(k&1) mul(f,f,a);
mul(a,a,a);
}
return f[0][0];
}