这里记录一下我 已经复习过的东西了 这次玩脱了 可就真的 回去了 要再认真一点。
第一个知识点:点双联通分量
先来一个点双联通分量 这几天 有模拟赛考这个了 但是我不太会写,qwq.类似于强连通分量的那种东西不过栈里要一直存一个割点。
值得一提的是 我把点双写成边双 写错好多次了这次下次一定不能错要分清什么时候点双什么时候边双。
LINK:牛客day2T2 点双
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define INF 1000000000 #define ll long long #define db double #define pb push_back #define un unsigned #define mod 1000000007 #define ull unsigned long long using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } //满堂花醉三千客 一剑霜寒十四州. const int MAXN=1000010; int n,m,k; int top,cnt,ans,rt,id,w,len=1; int q[MAXN]; int s[MAXN]; int dfn[MAXN],low[MAXN],vis[MAXN]; int lin[MAXN],ver[MAXN<<2],nex[MAXN<<2]; inline int cmp(int x,int y){return x>y;} inline void add(int x,int y) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; } inline void dfs(int x) { dfn[x]=low[x]=++cnt; s[++top]=x; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(!dfn[tn]) { dfs(tn); low[x]=min(low[x],low[tn]); if(low[tn]==dfn[x]) { int sz=0; for(int j;j!=tn;--top) { j=s[top]; ++sz; } ++sz; if(sz==2){if(k)--k,++ans;} else q[++id]=sz; } } else low[x]=min(low[x],dfn[tn]); } } int main() { //freopen("1.in","r",stdin); n=read();m=read();k=read(); for(int i=1;i<=m;++i) { int x,y; x=read();y=read(); add(x,y);add(y,x); } for(int i=1;i<=n;++i) if(!dfn[i]) { ++ans;top=0; dfs(i); } sort(q+1,q+1+id,cmp); for(int i=1;i<=id;++i) { if(!k)break; --k; w=min(k,q[i]-1); ans+=w; k-=w; } printf("%d ",ans); return 0; }
第二个知识点:欧拉降幂
欧拉降幂又又又忘了 真服了自己了 什么都忘。首先 进行欧拉降幂 线性筛得会吧 其实原理就是用最小的质因数去筛没什么。
然后 需要注意的是 暴力不断降幂的复杂度是logn的或者2logn的,证明其还是很显然的。
值得一提的是 降到1返回0 没降到边界到了直接快速幂搞定即可。配合上 快速幂或者gcd 复杂度就到了log方了。
一定注意开long long。
LINK:黄金妖精奈芙莲 欧拉降幂模板题
!!!高能预警 wa掉了 少考虑了一个东西 扩展欧拉定理的适用条件是 b>=phi(n) 然后才能 搞少考虑了情况 上次好像也是这个地方wa 下次不能再wa了。
考虑怎么改我们是必要知道后面的取值才能知道前面到底是否取模 一个比较简明的方法是 开结构体一个保存是否大于phi(n)一个保存在大于的情况下的结果。(这个做法我并不认可 想了40min 正确性有问题。或者说不严谨。
还是考虑一种慢一点的 但是比较没有争议的做法吧。我们直接暴力到在后面找几项暴力计算 由于>1的指数幂增长非常快(就算都是2也大于int了 所以是觉得正确的 但是这样的方法可以判定总体复杂度也就多加一点点。
出了点小bug 不过没关系 又仔细复习了一遍。复杂度还是log^2的 不过多一个常数(也就慢5倍吧/cy
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define INF 1000000000 #define ll long long #define db double #define pb push_back #define un unsigned #define ull unsigned long long using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=500010,maxn=20000010; int top,n,m,maxx=20000000,mark; ll a[MAXN],c[MAXN],b[MAXN]; int phi[maxn],v[maxn],p[maxn]; inline void add(int x,ll y) { while(x<=n) { c[x]+=y; x+=x&(-x); } } inline ll ask(int x) { ll ans=0; while(x) { ans+=c[x]; x-=x&(-x); } return ans; } inline ll ksm(ll b,ll p,ll mod) { ll ans=1; while(p) { if(p&1)ans=ans*b%mod; b=b*b%mod; p=p>>1; } return ans; } inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} inline ll fast_pow(ll b,ll p,ll mod) { ll ans=1; while(p) { if(b>=mod) { mark=1; return 1; } if(p&1)ans=ans*b; if(ans>=mod) { mark=1; return 1; } b=b*b; p=p>>1; } return ans; } inline ll dfs(int l,int r,int x) { if(x==1)return 0; int w=(a[l]+ask(l))%x;//当前数字 if(w==1)return 1; if(l==r)return w; ll res=0; int flag=gcd(w,x)==1?0:1; int last=min(l+6,r); for(int i=last;i>l;--i) { b[i]=a[i]+ask(i); if(b[i]==1)last=i; } int ww=1;mark=0; for(int i=last;i>l;--i) { ww=fast_pow(b[i],ww,phi[x]); if(mark)break; } if(mark)res=dfs(l+1,r,phi[x])+flag*phi[x]; else res=dfs(l+1,r,x); return ksm(w,res,x); } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=n;++i)a[i]=read(); for(int i=2;i<=maxx;++i) { if(!phi[i]) { p[++top]=i; phi[i]=i-1; v[i]=i; } for(int j=1;j<=top;++j) { if(p[j]>v[i]||i>maxx/p[j])break; v[i*p[j]]=p[j]; phi[i*p[j]]=i%p[j]?phi[i]*(p[j]-1):phi[i]*p[j]; } } for(int i=1;i<=m;++i) { int op,l,r,x; op=read();l=read();r=read();x=read(); if(op==1)add(l,x),add(r+1,-x); else printf("%lld ",dfs(l,r,x)); } return 0; }
第三个知识点:SG函数
LINK:博弈小问题 有向无环图的SG
询问 有向图游戏的和 一个比较显然 的思路是求出每一个游戏的SG异或一下 证明?我又复习了一下 证明是从 SG的本身来看的 。
比如 SG=k 那么这个局面可以到达0~k-1所有的局面 所以说也就是说对于一个就像NIM一样了异或一下即可。
对于这道题 一个比较显然的SG 赋值是出度为0的点SG就是0了必败局面 然后向上返更新SG即可。
这个过程可以使用dfs做当然可以反向拓扑写 这里直接dfs好了。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define RI register ll #define db double #define pii pair<ll,ll> #define mk make_pair #define mod 1000000007 #define ull unsigned long long using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } //一剑破万法 剑来. const int MAXN=2010,maxn=6010; int n,m,len,cnt,Q,ans; int lin[MAXN],ver[maxn],nex[maxn],e[maxn]; int sg[MAXN],vis[MAXN],f[MAXN],q[MAXN]; inline void add(int x,int y) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; } inline void dfs(int x) { vis[x]=1; int t=0; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(vis[tn])continue; dfs(tn); } for(int i=lin[x];i;i=nex[i])q[++t]=ver[i]; if(!t)sg[x]=0; else { for(int i=1;i<=t;++i)f[sg[q[i]]]=1; int w=0; while(f[w])++w; sg[x]=w; for(int i=1;i<=t;++i)f[sg[q[i]]]=0; } } int main() { freopen("1.in","r",stdin); n=read();m=read();Q=read(); for(int i=1;i<=m;++i) { int x,y; x=read();y=read(); add(x,y); } for(int i=1;i<=n;++i) { if(vis[i])continue; dfs(i); } //for(int i=1;i<=n;++i)cout<<sg[i]<<endl; for(int i=1;i<=Q;++i) { int x=read(); //cout<<sg[x]<<endl; ans=ans^sg[x]; } if(ans)puts("win"); else puts("lose"); return 0; }
第四个知识点:欧拉路 欧拉回路。
我怎么什么都忘了,这里我放一个 我自己的理解。首先是回溯的时候存点为了 让其连续 就是为了防止遇到终点的情况遇到环就没有什么大问题因为最后一定是跑到了终点 画图可得。
注欧拉路的 删边问题邻接矩阵直接减减 而邻接表则需要 lin[x]=nex[i] 注意 for是这样写的
for(int i=lin[x];i;i=lin[x])
然后 对于起点和终点 判断奇偶即可。
第五个知识点:斜率优化。
还是自己的优化套路 注意观察 dp的式子不要打错 尽量先写一个暴力 根据暴力来进行优化。
做了一道 独特的斜率优化 更新了 我斜率优化的观念 qwq. 是这样的 凸包斜率递减 所以 可以从后面排除决策 因为 后面的斜率一旦 小于当前斜率 且斜率但带哦递增。
那么必然没有什么用 一直排除到有效的地方即可。可以证明有效的地方只有一个qwq...
LINK:柠檬
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<ctime> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<algorithm> #include<vector> #include<cctype> #include<cstdlib> #include<utility> #include<bitset> #include<set> #include<map> #include<stack> #include<iomanip> #define INF 100000000000000000ll #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define db double #define EPS 1e-5 #define us unsigned #define mod 998244353 using namespace std; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const ll MAXN=100010,maxn=10010; ll n; vector<ll>q[maxn]; ll a[MAXN]; ll last[MAXN],sum[MAXN]; ll f[MAXN];//f[i]表示前i段分成若干次取得到的最大代价 //f[i]=max{f[j]+((j+1)~i)Sk*tk^2}; //需要枚举 j 和 k 考虑优化 //一个比较显然的结论是 选出的一段序列两端相等 //f[i]=max{f[j-1]+(sum[i]-sum[j]+1)^2*w[i]}; //斜率优化即可; //f[j-1]+sumj^2wj=fi-(sumi+1)^2*wi+2*sumjwjsumi; inline double slope(ll x,ll y) { return (1.0*sum[y]*sum[y]*a[y]+f[y-1]-sum[x]*sum[x]*a[x]-f[x-1])/(1.0*2*sum[y]*a[y]-2*sum[x]*a[x]); } int main() { //freopen("1.in","r",stdin); n=read(); for(ll i=1;i<=n;++i) { a[i]=read(); sum[i]=sum[last[a[i]]]+1; last[a[i]]=i; } for(ll i=1;i<=n;++i) { ll sz=q[a[i]].size(); while(sz>1&&slope(q[a[i]][sz-2],q[a[i]][sz-1])<=slope(q[a[i]][sz-1],i))q[a[i]].pop_back(),--sz; q[a[i]].push_back(i); sz=q[a[i]].size(); while(sz>1&&slope(q[a[i]][sz-2],q[a[i]][sz-1])<=sum[i]+1)q[a[i]].pop_back(),--sz; f[i]=f[q[a[i]][sz-1]-1]+(sum[i]-sum[q[a[i]][sz-1]]+1)*(sum[i]-sum[q[a[i]][sz-1]]+1)*a[i]; } printf("%lld ",f[n]); return 0; }
第六个知识点 错位排列
其实挺简单的 通项公式我听到现在都不知道是什么 算法 用处不是很大 递推式会就好了。值得注意的这并非线性齐次的递推式 所以不能矩阵乘法优化。
值得注意的式 d[0]=1 ; d[1] =0 ; d[2]=1;/cy 因此我爆零了一次。
第七个知识点 最小表示法
这道题是 观察一下发现很难做的样子 但是我们不断翻转一个圆盘发现了每一个位置 都是同时+1 或者 同时 -1 什么是 一直不变的东西?
考虑 他们之间 的距离是不变的 用这个 东西来判断是否相同即可。然后求一遍最小表示即可。
关于最小表示的 真正含义 我们指针不断向右移动 然后判断字典序 的大小即可每一次比较都是可以移动指针的 我们 每次移动指针都是可以判断一些 决策点是最不优的。
由于 i指针和j指针都只会往后移动一次 所以复杂度为O(n) 。
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 10000000000000ll #define ll long long using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=520; int n,m,p,ans; int a[MAXN][MAXN]; int b[MAXN<<1],c[MAXN<<1]; inline int calc(int *w) { int i=1,j=2,k; while(i<=m&&j<=m) { for(k=0;k<m&&w[i+k]==w[j+k];++k); if(k==n)break; if(w[i+k]>w[j+k]) { i=i+k+1; if(i==j)++i; } else { j=j+k+1; if(i==j)++j; } } return min(i,j); } int main() { //freopen("1.in","r",stdin); n=read();m=read();p=read(); for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j)b[j]=read(); sort(b+1,b+1+m); for(int j=2;j<=m;++j)c[j]=b[j]-b[j-1]; c[1]=p-b[m]+b[1]; for(int j=1;j<=m;++j)c[j+m]=c[j]; int pos=calc(c); for(int j=1;j<=m;++j)a[i][j]=c[pos+j-1]; } for(int i=1;i<=n;++i) { for(int j=i+1;j<=n;++j) { int flag=0; for(int k=1;k<=m;++k) { if(a[j][k]!=a[i][k]){flag=1;break;} } if(!flag)++ans; } } printf("%d ",ans); return 0; }
第八个知识点 精髓的贪心。
LINK:泡泡堂 悲愤时写的题目 wa了几次之后就清醒了。
首先 考虑一下能赢的场数尽可能的多 如果有输场 那么就利用 送人头策略即可。值得 注意的 是我们 发现 需要从最小的 开始判断。
然后 判断最小的能否胜利 能赢 的话就赢 会给答案不会带来更差的结果。不能赢的话 就可以考虑送人头了 平局可能不是最优的 因为 可能去送人头可以多加一个胜场 也可能平局 因为后面的人已经赢到现在了。
但是 我们经过 前面的排查 类似于 最大值能赢就能一直赢下去 如果 打不过了此时必然有输场此时 就可以 送人头了。一定 不会更差 。最小的判断是为了 送人头的时候 可以 和对方最大的差距尽量少 有可能拿到平局什么的。
所以这样判断即可。
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 10000000000000ll #define ll long long using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=100010; int n,cnt,ans; int a[MAXN],b[MAXN]; int main() { //freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;++i)a[i]=read(); for(int i=1;i<=n;++i)b[i]=read(); sort(a+1,a+1+n); sort(b+1,b+1+n); int l=1,r=n; int L=1,R=n; while(L<=R) { if(a[R]>b[r]) { ++ans; --R;--r;continue; } if(a[L]>b[l]) { ++ans; ++L;++l;continue; } if(a[L]==b[r])++cnt; ++L;--r; } printf("%d ",ans*2+cnt); ans=cnt=0; for(int i=1;i<=n;++i)swap(a[i],b[i]); l=1,r=n; L=1,R=n; while(L<=R) { if(a[R]>b[r]) { ++ans; --R;--r;continue; } if(a[L]>b[l]) { ++ans; ++L;++l;continue; } if(a[L]==b[r])++cnt; ++L;--r; } printf("%d ",2*n-ans*2-cnt); return 0; }
第九个知识点 三分法。
发现 我用三分 一直都是 骗分行为 qwq 暑假 网络流的 时候写过 写挂 期望题目骗分 利用三分找最优解 然后......
各种心酸的历程 然而每次三分 都是0分 而且 还经常容易 写挂 死循环每次都调的我头疼。这里还是记忆一个模板。每次调用就好多了。
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 10000000000000ll #define ll long long #define mod 1000000007 #define db double #define EPS 1e-7 using namespace std; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int MAXN=15; int n; db l,r; db b[MAXN]; inline db ask(db x) { db ans=0; for(int i=1;i<=n;++i) { ans=ans*x; ans+=b[i]; } return ans; } int main() { //freopen("1.in","r",stdin); scanf("%d",&n);++n; scanf("%lf%lf",&l,&r); for(int i=1;i<=n;++i)scanf("%lf",&b[i]); while(l+EPS<r) { db mid=l+(r-l)/3; db mid1=l+(r-l)/3*2; db w=ask(mid); db w1=ask(mid1); if(w>=w1)r=mid1; else l=mid; } printf("%.5lf",l); return 0; }
第十个知识点 二分图博弈。
LINK:游戏JSOI2009
精髓博弈 我见识了 一个结论 必然 在一个点 一直都存在于最大匹配之上那么对于先手来说必胜。显然
一个结论不是匹配点 或者 可以不在最大匹配上的点一定必败 。证明:显然。
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 10000000000000ll #define ll long long #define mod 1000000007 #define db double #define EPS 1e-7 using namespace std; const int MAXN=10010,maxn=110; int n,m,cnt,len,dfn,flag,top; int id[maxn][maxn],c[maxn][maxn],match[MAXN],vis[MAXN]; char a[maxn][maxn]; int lin[MAXN],ver[MAXN<<3],nex[MAXN<<3]; inline void add(int x,int y) { ver[++len]=y;nex[len]=lin[x];lin[x]=len; ver[++len]=x;nex[len]=lin[y];lin[y]=len; } inline int dfs(int x) { for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(vis[tn]==dfn)continue; vis[tn]=dfn; if(!match[tn]||dfs(match[tn])) { match[tn]=x; match[x]=tn; return 1; } } return 0; } int main() { //freopen("1.in","r",stdin); scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) { scanf("%s",a[i]+1); for(int j=1;j<=m;++j) { if(a[i][j]=='#')continue; c[i][j]=(i+j)&1; id[i][j]=++cnt; } } for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { if(c[i][j]) { //cout<<i<<' '<<j<<' '<<id[i][j]<<endl; if(id[i-1][j]) add(id[i][j],id[i-1][j]); if(id[i][j-1]) add(id[i][j],id[i][j-1]); if(id[i+1][j]) add(id[i][j],id[i+1][j]); if(id[i][j+1]) add(id[i][j],id[i][j+1]); } } } for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { if(!id[i][j])continue; if(c[i][j]) { ++dfn; dfs(id[i][j]); } } } //for(int i=1;i<=cnt;++i)printf("%d %d ",i,match[i]); for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { if(!id[i][j])continue; if(!match[id[i][j]]) { if(!flag)puts("WIN"); flag=1; printf("%d %d ",i,j); continue; } int L=match[id[i][j]]; match[id[i][j]]=0; match[L]=0; ++dfn;vis[id[i][j]]=dfn; if(dfs(L)) { if(!flag)puts("WIN"); flag=1;printf("%d %d ",i,j); } else match[id[i][j]]=L,match[L]=id[i][j]; } } if(!flag)puts("LOSE"); return 0; }
第十一个知识点 裴蜀定理 一直都没有认证审视过这个题目 所以 上来一道题就不会写qwq.
LINK:裴蜀定理
显然 对于 ax+by=c 这个方程有解 (a,b)|c 所以(a,b) 是其最小的解 对于 ax+by+cz+...=s s的最小解就是(a,b,c...);
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 10000000000000ll #define ll long long #define db double #define EPS 1e-7 #define mod 998244353 using namespace std; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,ans; inline int gcd(int a,int b){return b?gcd(b,a%b):a;} int main() { //freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;++i) { int x=read(); if(x<0)x=-x; ans=gcd(ans,x); } printf("%d ",ans); return 0; }
第十二个知识点 欧拉降幂 这个不能再错了 a和mod 互质的时候可以直接%phi(mod).而不互质的时候 必须 保证指数大于phi(mod) 才能 使用 。
需要提前判断一下。
LINK:欧拉定理
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 10000000000000ll #define ll long long #define db double #define EPS 1e-7 using namespace std; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } ll a,b,mod,s; ll phi,ans; inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} inline ll fast_pow(ll b,ll p) { ll cnt=1; while(p) { if(p&1)cnt=cnt*b%mod; b=b*b%mod; p=p>>1; } return cnt; } signed main() { //freopen("1.in","r",stdin); a=read();phi=s=mod=read(); for(ll i=2;i*i<=s;++i) if(s%i==0) { while(s%i==0)s/=i; phi=phi/i*(i-1); } if(s>1)phi=phi/s*(s-1); ll w=gcd(a,mod); if(w==1) { char c=getchar(); while(c>='0'&&c<='9') { ans=(ans*10+c-'0')%phi; c=getchar(); } printf("%lld ",fast_pow(a,ans)); } else { ll flag=0; char c=getchar(); while(c>='0'&&c<='9') { ans=ans*10+c-'0'; if(ans>phi) { flag=1; ans=ans%phi; } c=getchar(); } if(flag)ans+=phi; printf("%lld ",fast_pow(a,ans)); } return 0; }
第十三个知识点 卢卡斯定理 这个定理 p一定要是质数.
卢卡斯定理求组合数为什么要用卢卡斯定理求组合数而不是直接阶乘和逆元求组合数呢? 原因是这样的 (我以前竟然自动忽略掉了这一点 还好复习的时候发现了这个盲点. 吓死我了qwq.
因为 在平常的 求组合数之中 模数较大 所以 C(n,m) 中不会包含 mod 这个因子 如果包含了我们就不能分子分母分开计算了因为上面 被取模之后 变成0 下面也同理。
但是 可能上下约分就解决了这个事情但是我们分开处理就出现了错误。这里 考虑 卢卡斯定理的话 p是一个质数 利用定理 把组合数的n m降到p之下 然后 再利用 阶乘和逆元计算即可。
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 10000000000000ll #define ll long long using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const ll MAXN=100010; ll n,m,mod,T; ll jc[MAXN],cnt; inline ll fast_pow(ll b,ll p) { ll ans=1; while(p) { if(p&1)ans=ans*b%mod; b=b*b%mod; p=p>>1; } return ans; } inline ll Lucas(ll a,ll b) { if(a<b)return 0; if(a<mod&&b<mod)return jc[a]*fast_pow(jc[b],mod-2)%mod*fast_pow(jc[a-b],mod-2)%mod; return Lucas(a%mod,b%mod)*Lucas(a/mod,b/mod)%mod; } signed main() { //freopen("1.in","r",stdin); T=read(); while(T--) { n=read();m=read();mod=read(); jc[0]=1; for(ll i=1;i<=n+m;++i)jc[i]=jc[i-1]*i%mod; printf("%lld ",Lucas(n+m,m)); } return 0; }
复杂度显然是 log(p) {n+m}*logp的 很快 但是处理阶乘 是n+m的 。
考虑 直接利用逆元求 我们发现 影响的只是 p 这个因子 我们对于每一个数就提出了p的若干次幂 进行上下约分即可。
复杂度的话 至少上界是 nlogp 也跑得很快 最后注意一下上下有没有约分完全即可。
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 10000000000000ll #define ll long long using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } ll n,m,mod,T; ll jc,jc1,jc2,cnt; inline ll fast_pow(ll b,ll p) { ll ans=1; while(p) { if(p&1)ans=ans*b%mod; b=b*b%mod; p=p>>1; } return ans; } inline int check(int x,int y) { while(x%mod==0) { x/=mod; cnt+=y; } return x; } signed main() { //freopen("1.in","r",stdin); T=read(); while(T--) { n=read();m=read();mod=read(); jc=jc1=jc2=1;cnt=0; for(ll i=1;i<=n+m;++i) { if(i<=n)jc1=jc1*check(i,-1)%mod; if(i<=m)jc2=jc2*check(i,-1)%mod; jc=jc*check(i,1)%mod; } printf("%lld ",cnt?0:jc*fast_pow(jc1,mod-2)%mod*fast_pow(jc2,mod-2)%mod); } return 0; }
第十四个知识点 乘法逆元。
关于O(n) 求逆元 我不知道写过多少次 了大体上就是列出来 和以前求出过的逆元的值有关的式子 然后线性的递推即可。
值得注意的式 乘法逆元的存在 当且仅当 数字 b 和 模数 mod 互质时存在 特别当 当 mod 为质数可以直接使用费马小定理 求逆。
特别的 如果不是质数 我们递推逆元会超时的时候 考虑 用exgcd来求解同余方程即可。
LINK:乘法逆元2 这道题 呢 我们不能 线性推 因为 不是连续的复杂度可能达到 1e9 也不能直接暴力费马小定理 nlogn 也过不去。
这里有一个比较新奇的思路是求出所有的 数的乘积的逆元然后 对于某个数字的逆元来说其实就是前缀积*后缀积*总逆元了。
复杂度就被降到O(n) 了很神奇 qwq.
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 10000000000000ll #define ll long long using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const ll MAXN=5000010; ll n,k,m,mod,ans; ll a[MAXN],s[MAXN],b[MAXN]; inline ll fast_pow(ll b,ll p) { ll ans=1; while(p) { if(p&1)ans=ans*b%mod; b=b*b%mod; p=p>>1; } return ans; } signed main() { //freopen("1.in","r",stdin); n=read();mod=read();k=m=read(); s[0]=1;b[n+1]=1; for(int i=1;i<=n;++i) { a[i]=read(); s[i]=s[i-1]*a[i]%mod; } for(int j=n;j>=1;--j)b[j]=b[j+1]*a[j]%mod; ll in=fast_pow(s[n],mod-2); for(int i=1;i<=n;++i) { ll w=s[i-1]*in%mod*b[i+1]%mod; ans=(ans+m*w%mod)%mod; m=m*k%mod; } printf("%lld ",ans); return 0; }
知识点复习完了 也该开始了。