最後的複習

  這裏記錄一下我 已經複習過的東西了 此次玩脫了 可就真的 回去了 要再認真一點。ios

第一個知識點:點雙聯通份量c++

先來一個點雙聯通份量 這幾天 有模擬賽考這個了 可是我不太會寫,qwq.相似於強連通份量的那種東西不過棧裏要一直存一個割點。算法

值得一提的是 我把點雙寫成邊雙 寫錯好屢次了此次下次必定不能錯要分清何時點雙何時邊雙。網絡

LINK:牛客day2T2 點雙ide

//#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\n",ans);
    return 0;
}
View Code

第二個知識點:歐拉降冪函數

歐拉降冪又又又忘了 真服了本身了 什麼都忘。首先 進行歐拉降冪 線性篩得會吧 其實原理就是用最小的質因數去篩沒什麼。優化

而後 須要注意的是 暴力不斷降冪的複雜度是logn的或者2logn的,證實其仍是很顯然的。spa

值得一提的是 降到1返回0 沒降到邊界到了直接快速冪搞定便可。配合上 快速冪或者gcd 複雜度就到了log方了。3d

必定注意開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\n",dfs(l,r,x));
    }
    return 0;
}
View Code

 第三個知識點: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;
}
View Code

 第四個知識點:歐拉路 歐拉回路。

我怎麼什麼都忘了,這裏我放一個 我本身的理解。首先是回溯的時候存點爲了 讓其連續 就是爲了防止遇到終點的狀況遇到環就沒有什麼大問題由於最後必定是跑到了終點 畫圖可得。

注歐拉路的 刪邊問題鄰接矩陣直接減減 而鄰接表則須要 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\n",f[n]);
    return 0;
}
View Code

第六個知識點 錯位排列

其實挺簡單的 通項公式我聽到如今都不知道是什麼 算法 用處不是很大 遞推式會就行了。值得注意的這並不是線性齊次的遞推式 因此不能矩陣乘法優化。

值得注意的式 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\n",ans);
    return 0;
}
View Code

第八個知識點 精髓的貪心。

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\n",2*n-ans*2-cnt);
    return 0;
}
View Code

第九個知識點 三分法。

發現 我用三分 一直都是 騙分行爲 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;
}
View Code

第十個知識點 二分圖博弈。

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\n",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\n",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\n",i,j);
            }
            else match[id[i][j]]=L,match[L]=id[i][j];
        }
    }
    if(!flag)puts("LOSE");
    return 0;
}
View Code

第十一個知識點 裴蜀定理 一直都沒有認證審視過這個題目 因此 上來一道題就不會寫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\n",ans);
    return 0;
}
View Code

第十二個知識點 歐拉降冪 這個不能再錯了 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\n",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\n",fast_pow(a,ans));
    }
    return 0;
}
View Code

第十三個知識點 盧卡斯定理 這個定理 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\n",Lucas(n+m,m));
    }
    return 0;
}
View Code

複雜度顯然是 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\n",cnt?0:jc*fast_pow(jc1,mod-2)%mod*fast_pow(jc2,mod-2)%mod);
    }
    return 0;
}
View Code

第十四個知識點 乘法逆元。

關於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\n",ans);
    return 0;
}
View Code
相關文章
相關標籤/搜索