蓝桥备赛第四周 同余+并查集
生活随笔
收集整理的這篇文章主要介紹了
蓝桥备赛第四周 同余+并查集
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 0x33 同余
- 同余類+剩余系+費馬小定理+歐拉定理及推論
- 最幸運的數字
- 題解
- 這次的代碼很多東西:歐拉函數快速求解,gcd,快速乘,各種定理,建議當模板背
- 10LL 轉換成長整型
- 快速乘+快速冪(取模)模板
- 技巧:試除法可以一半一半求,減少遍歷次數,兩半都用稠密的一半遍歷(因子是成對的)
- 擴展歐幾里得算法
- ax+by=gcd(a,b)存在至少一組整數解xy
- 乘法逆元
- sumdiv
- 新的逆元做法,下次再做吧
- 線性同余方程
- 同余方程
- 就是exgcd的模板題
- 中國剩余定理
- 表達整數的奇怪方式
- 思路
- 看題解更好些
- 沒法一口氣求解,只能一個一個往上增加
- 0x41并查集
- 這個東西很適合搞圖的連通性==“擅長維護傳遞性”
- 模板
- 程序自動分析
- 標準的并查集題
- 不想離散化,所以map版并查集,但會有一組TLE
- 在map基礎上,把相等的和不等的分開存儲,空間*2,時間/2,牛客能過了,acwing多過了幾條數據,但還是7組
- 我透了,改個unordered_map就能過,或者開O2(O2不能過)
- 2021.3.7復習解法(能過前九個):有個很不錯的結構體寫法
- supermarket
- 擴展域與邊帶權的并查集
- 銀河英雄傳說
- 雖然dx的變化有一半是寫在get里面的,但如果不merge,fa[x]指向的是當前根節點,根節點dx為0,dx+=d【fa【x】】不會發生改變-----------每一次merge,對每個節點只+1次,且不會影響其他同集合的節點
- 帶圖的題解
- 另外有的標準size是關鍵字,不能用,不過編譯
- 銀河英雄傳說---邊權集2021.3.8復習---有注釋!
- 2021.4.17研究出來的模板
- 奇偶游戲
- 邊權集解法
- 先將正常數組改成前綴和,變成兩個點的奇偶性,這樣“權”就可以變成d[x](x與根之間),奇偶性的話,就用抑或來計算,如果已經有了,那么通過dx來計算x與y之間奇偶性,如果沒有聯通,那么要通過已知x與y之間奇偶性,倒推兩個根節點之間的奇偶性,合并,另外數很稀疏,考慮離散化
- acwing秦淮河大佬的復現書上代碼,思路很棒
- 擴展域解法(這個很簡單,維護兩個域,互相也是聯通的,不用算權值了)
- 擴展域解法1.不要忘了初始化2.不要忘了-1 3.擴展域一定要放一個數組里 ------------2021.3.8復習
- 食物鏈
- 拓展域
- 三個域
- 2021.3.8復習:這里的捕食域,同類域,天敵域,通過三個域表達出了方向關系(X被Y捕食,Y是X的天敵):通過X的Xself 與 Y的 Yeat 聯通
- 邊帶權
- 把吃,被吃,同類設為 1,2,0,每次維護就好
0x33 同余
同余類+剩余系+費馬小定理+歐拉定理及推論
最幸運的數字
現在給定一個正整數L,請問至少多少個8連在一起組成的正整數 (即最小幸運數字)是L的倍數。 輸入格式 輸入包含多組測試用例。 每組測試用例占一行,包含一個整數L。 當輸入用例L=0時,表示輸入終止,該用例無需處理。 輸出格式 每組測試用例輸出結果占一行。 結果為“Case 1: ”+一個整數N,N代表滿足條件的最小幸運數字的位數。 如果滿足條件的幸運數字不存在,則N=0。 數據范圍 1≤L≤2?109 輸入樣例: 8 11 16 0 輸出樣例: Case 1: 1 Case 2: 2題解
這次的代碼很多東西:歐拉函數快速求解,gcd,快速乘,各種定理,建議當模板背
10LL 轉換成長整型
快速乘+快速冪(取模)模板
技巧:試除法可以一半一半求,減少遍歷次數,兩半都用稠密的一半遍歷(因子是成對的)
#include"stdio.h" #include"string.h" #include"math.h" #include"algorithm" using namespace std; typedef long long ll;ll L;ll gcd(ll a,ll b) {if(a < b){ll t = a; a = b; b = t;}if(b == 0)return a;return gcd(b,a%b); } ll multi(ll a,ll b,ll mod) {ll ans = 0;while(b){if(b & 1)ans = (ans + a) % mod;a = (a << 1) % mod;b = b >> 1;}return ans; } ll Ola(ll n) {ll sum = n;for(int i = 2; i * i<= n; i ++){if(n % i)continue;sum = sum / i * (i - 1);while(n % i == 0)n = n / i;}if(n != 1)sum = sum / n * (n - 1);return sum; } int Quick(ll a,ll b,ll mod) ///a:10LL b:歐拉函數的因子,也就是次數 mod:9L/d ///這個就是快速冪,套了快乘的快速冪(因為mod可能很大) {ll ans = 1; a %= mod;///a與n互質///a %= mod;不加也能過while(b){if(b & 1)ans = multi(ans,a,mod);b >>= 1;a = multi(a,a,mod);}if(ans == 1)return 1;return 0; } ll solve() {ll g = L / gcd(L,8LL) * 9;///LL轉換,g是9L除以dif(gcd(10LL,g) != 1) return 0;///a,n互質才有解ll sum = 1;ll num = Ola(g); ///把g的歐拉函數求出來///下面這個for是試除法求因子for(int i = 1; i * (ll)i <= num; i ++){if(num % i) continue;///是因子就看看能不能余g///這個quick有個10LL的參數應該也是模板if(Quick(10LL,(ll)i,g) == 1)return i;}///依舊是試除法求因子,不過求的是大于根號的這一半(因為小于根號這一半較小嘛,更好求)ll m = sqrt(num);for(int i = m; i >= 1; i --){if(num % i == 0 && Quick(10LL,num / i,g) == 1)return num / i;}return 0; }int main() {int cnt = 1;while(~scanf("%lld",&L)){if(L == 0) break;printf("Case %d: %lld\n",cnt ++,solve());} }擴展歐幾里得算法
ax+by=gcd(a,b)存在至少一組整數解xy
乘法逆元
sumdiv
新的逆元做法,下次再做吧
線性同余方程
同余方程
求關于x的同余方程 ax ≡ 1(mod b) 的最小正整數解。輸入格式 輸入只有一行,包含兩個正整數a,b,用一個空格隔開。輸出格式 輸出只有一行,包含一個正整數x,表示最小正整數解。輸入數據保證一定有解。數據范圍 2≤a,b≤2?109 輸入樣例: 3 10 輸出樣例: 7就是exgcd的模板題
中國剩余定理
表達整數的奇怪方式
給定 2n 個整數a1,a2,…,an和m1,m2,…,mn,求一個最小的非負整數 x,滿足?i∈[1,n],x≡mi(mod ai)。輸入格式 第1 行包含整數 n。第 2..n+1行:每 i+1 行包含兩個整數ai和mi,數之間用空格隔開。輸出格式 輸出最小非負整數 x,如果 x 不存在,則輸出 ?1。 如果存在 x,則數據保證 x 一定在64位整數范圍內。數據范圍 1≤ai≤231?1, 0≤mi<ai 1≤n≤25 輸入樣例: 2 8 7 11 9 輸出樣例: 31思路
看題解更好些
沒法一口氣求解,只能一個一個往上增加
0x41并查集
這個東西很適合搞圖的連通性==“擅長維護傳遞性”
模板
int fa[maxn]; void init() {for(int i=0;i<maxn;i++){fa[maxn]=i;} } int get(int x) {if(x==fa[x])return x;return fa[x]=get(fa[x]); } int mergefa(int x,int y) {fa[get(x)]=get(y); }程序自動分析
標準的并查集題
不想離散化,所以map版并查集,但會有一組TLE
#include <iostream> #include<bits/stdc++.h> using namespace std; typedef long long ll; #define maxn 1000005 map<int,int> fa; int n; int xi[maxn]; int yi[maxn]; int ei[maxn]; int get(int x) {if(fa[x]==0||x==fa[x])///這個if應該是短路運算符///不過不加fa[x]==0||也行,已經把賦初值拆分了///map沒賦過值的為0{fa[x]=x;///給沒賦過值的準備的return x;}return fa[x]=get(fa[x]); } void mergefa(int x,int y) {fa[get(x)]=get(y); } int main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t;cin>>t;while(t--){fa.clear();///初始化要做好int n;cin>>n;for(int i=0;i<n;i++){cin>>xi[i]>>yi[i]>>ei[i];}///先算一遍等于號的for(int i=0;i<n;i++){int x=xi[i];int y=yi[i];if(ei[i]==1){if(fa[x]==0){fa[x]=x;///賦初值拆分進來}if(fa[y]==0){fa[y]=y;///賦初值拆分進來}//mergefa(x,y);}}///不等于的int flag=1;for(int i=0;i<n;i++){int x=xi[i];int y=yi[i];if(ei[i]==0){if(fa[x]==0){fa[x]=x;///賦初值拆分進來}if(fa[y]==0){fa[y]=y;///賦初值拆分進來}if(get(x)==get(y)){flag=0;break;}else{continue;}}}if(flag){cout<<"YES"<<endl;}else{cout<<"NO"<<endl;}}return 0; }在map基礎上,把相等的和不等的分開存儲,空間*2,時間/2,牛客能過了,acwing多過了幾條數據,但還是7組
#include <iostream> #include<bits/stdc++.h> using namespace std; typedef long long ll; #define maxn 1000005 map<int,int> fa; int n; int xi[maxn]; int yi[maxn];int xp[maxn]; int yp[maxn]; int get(int x) {if(x==fa[x])///這個if應該是短路運算符///不過不加fa[x]==0||也行,已經把賦初值拆分了///map沒賦過值的為0{fa[x]=x;///給沒賦過值的準備的return x;}return fa[x]=get(fa[x]); } void mergefa(int x,int y) {fa[get(x)]=get(y); } int main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t;cin>>t;while(t--){fa.clear();///初始化要做好int n;cin>>n;int e;int bufx,bufy,bufe;int cnt1,cnt0;cnt1=cnt0=0;for(int i=0;i<n;i++){cin>>bufx>>bufy>>bufe;if(bufe==1){xi[cnt1]=bufx;yi[cnt1++]=bufy;}if(bufe==0){xp[cnt0]=bufx;yp[cnt0++]=bufy;}}for(int i=0;i<cnt1;i++){int x=xi[i];int y=yi[i];if(fa[x]==0){fa[x]=x;///賦初值拆分進來}if(fa[y]==0){fa[y]=y;///賦初值拆分進來}//mergefa(x,y);}///不等于的int flag=1;for(int i=0;i<cnt0;i++){int x=xp[i];int y=yp[i];if(fa[x]==0){fa[x]=x;///賦初值拆分進來}if(fa[y]==0){fa[y]=y;///賦初值拆分進來}if(get(x)==get(y)){flag=0;break;}else{continue;}}if(flag){cout<<"YES"<<endl;}else{cout<<"NO"<<endl;}}return 0; }我透了,改個unordered_map就能過,或者開O2(O2不能過)
2021.3.7復習解法(能過前九個):有個很不錯的結構體寫法
#include <iostream> #include<bits/stdc++.h> using namespace std; typedef long long ll; #define maxn 1000005 int fa[maxn]; struct uneq{int a;int b; }uneqs[maxn]; void initfa() {for(int i=0;i<maxn;i++){fa[i]=i;}} int getfa(int x) {if(fa[x]==x)return fa[x];return fa[x]= getfa(fa[x]); } int mergefa(int a,int b) {fa[getfa(a)]=getfa(b); } int main() {int t;cin>>t;while(t--){initfa();int n;cin>>n;int cntuneq=0;int bufa,bufb,bufc;while(n--){cin>>bufa>>bufb>>bufc;if(bufc==1){mergefa(bufa,bufb);}if(bufc==0){uneqs[cntuneq++]={bufa,bufb};///新的結構體寫法很有趣}} // for(int i=0;i<cntuneq;i++) // { // cout<< uneqs[i].a<<uneqs[i].b; // }int flag=1;for(int i=0;i<cntuneq;i++){if(getfa(uneqs[i].a)==getfa(uneqs[i].b)){flag=0;break;}}if(flag){cout<<"YES"<<endl;}else{cout<<"NO"<<endl;}}return 0; }supermarket
0x17
擴展域與邊帶權的并查集
銀河英雄傳說
雖然dx的變化有一半是寫在get里面的,但如果不merge,fa[x]指向的是當前根節點,根節點dx為0,dx+=d【fa【x】】不會發生改變-----------每一次merge,對每個節點只+1次,且不會影響其他同集合的節點
帶圖的題解
另外有的標準size是關鍵字,不能用,不過編譯
#include <bits/stdc++.h> using namespace std; const int N=31000+10; int fa[N],n,t,i,j,d[N],size[N];//size就是記錄個數 int get(int x) {if (x==fa[x])return x;int root=get(fa[x]);d[x]+=d[fa[x]];//往下推進return fa[x]=root; } void merge(int x,int y) {x=get(x),y=get(y);fa[x]=y,d[x]=size[y];size[y]+=size[x];//順帶記錄 } int main() {scanf("%d\n",&t);for(i=1;i<=30000;i++)fa[i]=i,size[i]=1;while(t--){char ch=getchar();scanf("%d %d\n",&i,&j);if (ch=='M'){merge(i,j);}else{if (get(i)==get(j))cout<<abs(d[i]-d[j])-1;elsecout<<"-1";cout<<endl;}}return 0; }銀河英雄傳說—邊權集2021.3.8復習—有注釋!
#include <iostream> #include<bits/stdc++.h> using namespace std; typedef long long ll; #define maxn 100005 int fa[maxn]; int d[maxn];///記錄戰艦x與fa[x]之間邊的權值///初值為0 int sizefa[maxn];/// 記錄樹的大小,初值1 void initfa() {for(int i=0;i<maxn;i++){fa[i]=i;}} int getfa(int x) {if(fa[x]==x)return x;return fa[x]=getfa(fa[x]); } int newgetfa(int x) {if(fa[x]==x)return x;int root=getfa(fa[x]);///先存出來,集合代表d[x]+=d[fa[x]]///樹根變成了新的樹根,----對邊權求和///到維護前樹根的距離+維護前樹根到新樹根的距離return fa[x]=root;} void mergefa(int a,int b) {fa[getfa(a)]=getfa(b); } void mergefa(int a,int b)///之前提到了需要+維護前樹根到新樹根的距離 { ///那么這個距離需要兩個步驟d[getfa(a)]=fasize(getfa(b));///1.接到尾部,舊樹根的邊權d[x](到新樹根距離)就是目標樹的size(邊數)fasize[get(fa(b))]+=fasize[getfa(b)];///2.成為新樹,size變大fa[getfa(a)]=getfa(b);} int main() {return 0; }2021.4.17研究出來的模板
#include <iostream> #include<bits/stdc++.h> using namespace std; typedef long long ll; #define maxn 1005 int fa[maxn]; int d[maxn]; void initfa() {for(int i=1;i<=n;i++){fa[i]=i;} } int getfa(int x) {if(fa[x]==x)return x;///這兩行就是維護權值數組int root=fa[x];d[x]+=d[fa[x]];return fa[x]=root; } void mergefa(int x,int y) {///這行是方便使用rx=getfa(x);ry=getfa(y);///這兩行是維護權值數組d[rx]=d[ry]+1;//d[rx]=size(y);sizem[y]+=sizem[x];///這行是原來的fa[x]=ry;}} int main() {return 0; }奇偶游戲
邊權集解法
先將正常數組改成前綴和,變成兩個點的奇偶性,這樣“權”就可以變成d[x](x與根之間),奇偶性的話,就用抑或來計算,如果已經有了,那么通過dx來計算x與y之間奇偶性,如果沒有聯通,那么要通過已知x與y之間奇偶性,倒推兩個根節點之間的奇偶性,合并,另外數很稀疏,考慮離散化
acwing秦淮河大佬的復現書上代碼,思路很棒
#include<bits/stdc++.h> using namespace std; const int N=10010<<1; struct node {int l,r,ans; } q[N]; int a[N],fa[N],d[N],n,m,t_n; int get(int x) {if (x==fa[x])return x;int root=get(fa[x]);d[x]^=d[fa[x]];//異或return fa[x]=root; } inline int read_init()//離散化 {cin>>n>>m;for(int i=1;i<=m;i++){char str[5];scanf("%d%d%s",&q[i].l,&q[i].r,str);q[i].ans=(str[0]=='e'?0:1);a[++t_n]=q[i].l-1;a[++t_n]=q[i].r;}sort(a+1,a+1+t_n);n=unique(a+1,a+1+t_n)-a-1;//去重 } inline int work() {read_init();for(int i=1;i<=n;i++)fa[i]=i;for(int i=1;i<=m;i++){int x=lower_bound(a+1,a+1+n,q[i].l-1)-a;//離散化后要找數int y=lower_bound(a+1,a+1+n,q[i].r)-a;int p=get(x),q2=get(y);if (p==q2){if ((d[x]^d[y])!=q[i].ans)//變量要相等,但是卻不相等了.{cout<<i-1<<endl;return 0;}}else{fa[p]=q2;//合并merge.兩方代碼就懶得寫函數了,見諒d[p]^=d[x]^d[y]^q[i].ans;//統統異或}}cout<<m;//數據過于優秀,一個問題都沒有 } int main() {work();return 0; }擴展域解法(這個很簡單,維護兩個域,互相也是聯通的,不用算權值了)
擴展域解法1.不要忘了初始化2.不要忘了-1 3.擴展域一定要放一個數組里 ------------2021.3.8復習
#include <iostream> #include<bits/stdc++.h> using namespace std; typedef long long ll; #define maxn 100005 int fa[maxn];///擴展域應該放在同一個數組里使用,x,x+n,x+n+n,x+n+n+n,......這樣才能測試是否聯通 int d[maxn]; int sizefa[maxn]; void initfa() {for(int i=0;i<maxn;i++){fa[i]=i;}} int getfa(int x) {if(fa[x]==x)return x;return fa[x]=getfa(fa[x]); } void mergefa(int a,int b) {fa[getfa(a)]=getfa(b); } int main() {initfa();int n;cin>>n;int t;cin>>t;int t2=t;int a,b,op;while(t--){cin>>a>>b>>op;if(op==0)///even{if(getfa(a-1)==getfa(b+n))///is odd--矛盾{cout<<t2-t<<'*';return 0;}mergefa(a-1,b);mergefa(a-1+n,b+n);}else{if(getfa(a-1)==getfa(b)){cout<<t2-t<<"#";return 0;}mergefa(a-1,b+n);mergefa(a-1+n,b);}}return 0; }食物鏈
拓展域
三個域
//這里我們將三個域,分別轉化為了n,n+n,n+n+n.因為讀入方面特別煩. #include <bits/stdc++.h> using namespace std; int fa[200000]; int n,m,k,x,y,ans; int get(int x) {if(x==fa[x])return x;return fa[x]=get(fa[x]); } void merge(int x,int y) {fa[get(x)]=get(y); } int main() {cin>>n>>m;for(int i=1;i<=3*n;i++)fa[i]=i;for(int i=1;i<=m;i++){scanf("%d%d%d",&k,&x,&y);if(x>n || y>n)ans++;///假話總數+1else if(k==1)///同類{if(get(x)==get(y+n) || get(x)==get(y+n+n))//如果x,y是同類,但是x是y的捕食中的動物,或者x是y天敵中的動物,那么錯誤.ans++;///假話總數+1else{merge(x,y);merge(x+n,y+n);merge(x+n+n,y+n+n);}}else///X捕食Y{if(x==y || get(x)==get(y) || get(x)==get(y+n))//x就是y,或者他們是同類,再或者是y的同類中有xans++;//都是假話else{merge(x,y+n+n);//y的天敵域加入xmerge(x+n,y);//x的捕食域加入ymerge(x+n+n,y+n);//x的天敵域是y的捕食域.}}}cout<<ans<<endl; } //x是同類域. //x+n是捕食域 //x+n+n是天敵域2021.3.8復習:這里的捕食域,同類域,天敵域,通過三個域表達出了方向關系(X被Y捕食,Y是X的天敵):通過X的Xself 與 Y的 Yeat 聯通
邊帶權
把吃,被吃,同類設為 1,2,0,每次維護就好
#include<bits/stdc++.h> using namespace std; const int maxn = 5e4 + 233; int fa[maxn], d[maxn]; int ff(int x) {if(fa[x] == x) return x;int r = ff(fa[x]);d[x] += d[fa[x]];return fa[x] = r; } int main() {int n,k; cin >> n >> k;for(int i = 0; i <= n; i++) fa[i] = i;int ans = 0;for(int i = 1; i <= k; i++){int t, a, b;scanf("%d%d%d", &t, &a, &b);if(a > n || b > n) {ans ++; continue;}else if(t == 2 && a == b) {ans++; continue;}else{int rel;if(t == 2) rel = 1;else rel = 0;int x = ff(a), y = ff(b);if(x == y) {if((((d[a] - d[b]) % 3) + 3) % 3 != rel)ans++;}else{fa[x] = y;d[x] = d[b] - (d[a] - rel);}}}cout<< ans; }作者:這個顯卡不太冷 鏈接:https://www.acwing.com/solution/content/1357/ 來源:AcWing 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。總結
以上是生活随笔為你收集整理的蓝桥备赛第四周 同余+并查集的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 蓝桥备赛第三周 倍增+贪心+素数+约数
- 下一篇: 2021蓝桥直播课-软件类-本科组