題目一node
算法分類:並查集,DFS,Tarjan算法c++
原題:算法
Time Limit: 2000/1000 MS (Java/Others)數組
Memory Limit: 32768/32768 K (Java/Others)less
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e5+5; 4 int s[maxn],height[maxn],val[maxn]; 5 int n,m; 6 inline int read(){ 7 register int x=0,f=1;char c=getchar(); 8 while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();} 9 while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar(); 10 return x*f; 11 } 12 int find_set(int x){ 13 return x==s[x]?x:find_set(s[x]); 14 } 15 void init_set(){ 16 for(int i = 1; i <= n; i++){ 17 s[i] = i; 18 height[i]=0; 19 } 20 } 21 void union_set(int x1, int y1,int l){ //將聯通的兩個節點按秩合併 22 int x = find_set(x1); 23 int y = find_set(y1); 24 if (height[x] == height[y]) { 25 height[x] = height[x] + 1; 26 s[y1] = x1; 27 val[y1]=l; 28 } 29 else{ 30 if (height[x]<height[y]) s[x1]=y1,val[x1]=l; 31 else s[y1] = x1,val[y1]=l; 32 } 33 if(s[y1]==x1) { 34 int t=y1; 35 while(t!=s[t]){ 36 height[s[t]]=max(height[s[t]],height[t]+1); 37 t=s[t]; 38 } 39 } 40 else{ 41 int t=x1; 42 while(t!=s[t]){ 43 height[s[t]]=max(height[s[t]],height[t]+1); 44 t=s[t]; 45 } 46 } 47 } 48 int main(){ 49 int t; 50 t=read(); 51 while(t--){ 52 n=read();m=read(); 53 n-=1; 54 init_set(); 55 while(n--){ 56 int x,y,v; 57 x=read(); 58 y=read(); 59 v=read(); 60 union_set(x,y,v); 61 } 62 while(m--){ 63 int x,y,k; 64 x=read();y=read(); 65 long long cnt=0; 66 if(height[y]>height[x]){ //將不在同一高度的兩個節點移動至同一個高度 67 while(height[x]!=height[y]){ 68 cnt+=val[x]; 69 x=s[x]; 70 } 71 } 72 else if(height[y]<height[x]){ 73 while(height[y]!=height[x]){ 74 cnt+=val[y]; 75 y=s[y]; 76 } 77 } 78 else{} 79 if(x==y){ 80 printf("%lld\n",cnt);continue; //若是兩點已經重合,輸出結果 81 } 82 else{ //將兩點同時上移,直至兩點重合 83 while(x!=y){ 84 cnt+=val[x]+val[y]; 85 x=s[x]; 86 y=s[y]; 87 } 88 } 89 printf("%lld\n",cnt); 90 } 91 } 92 return 0; 93 }
雖然這道題目只有4e4的數據,而且給了兩秒的時間,可是這樣暴力的作法仍是超時了。本身作了很多改進仍是沒有任何效果。因此這道題目得推翻一開始的作法從新來。函數
想了很久沒有思路,後來經過幾篇博客,瞭解到了Tarjan算法。ui
先來講一說Tarjan算法:this
Tarjan算法其實是一種離線算法。所謂離線,就是經過一次遍歷一次性將全部詢問結果進行計算並進行保存。最後不須要再回到圖中計算直接輸出結果。其時間複雜度爲O(n+q);spa
n爲全部節點的總個數,q爲全部詢問的個數。3d
Tarjin算法的基本流程大概是:
1.任選一個節點爲根節點,以這個節點爲起點
2.遍歷該點u全部子節點v,並標記這些子節點v已被訪問過。
3.如果v還有子節點,返回2,不然下一步。
4.將v的祖先記爲u。
5.尋找與當前點u有詢問關係的點v。如果v已經被訪問過了,則能夠確認u和v的最近公共祖先爲v被合併到的父親節點。若是v 沒有被訪問,則繼續下一步操做
其實畫一個圖本身來手動模擬會顯得更加直觀
如今有這樣一個圖:
fa[1]=1,vis[1]=false 如今有兩組點須要詢問,要求這兩個點的最近公共祖先
fa[2]=2,vis[2]=false 分別爲點2和點4,點8與點5
fa[3]=3,vis[3]=false
fa[4]=4,vis[4]=false
fa[5]=5,vis[5]=false
fa[6]=6,vis[6]=false
fa[7]=7,vis[7]=false
fa[8]=8,vis[8]=false
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N=40000+5; 4 struct Edge{ 5 int cnt,x[N],y[N],z[N],nxt[N],fst[N]; 6 void set(){ 7 cnt=0; 8 memset(x,0,sizeof x); 9 memset(y,0,sizeof y); 10 memset(z,0,sizeof z); 11 memset(nxt,0,sizeof nxt); 12 memset(fst,0,sizeof fst); 13 } 14 void add(int a,int b,int c){ 15 x[++cnt]=a; 16 y[cnt]=b; 17 z[cnt]=c; 18 nxt[cnt]=fst[a]; 19 fst[a]=cnt; 20 } 21 }e,q; 22 int T,n,m,from,to,dist,in[N],rt,dis[N],fa[N],ans[N]; 23 bool vis[N]; 24 void dfs(int rt){ 25 for (int i=e.fst[rt];i;i=e.nxt[i]){ 26 dis[e.y[i]]=dis[rt]+e.z[i]; 27 dfs(e.y[i]); 28 } 29 } 30 int getf(int k){ 31 return fa[k]==k?k:fa[k]=getf(fa[k]); 32 } 33 void LCA(int rt){ 34 for (int i=e.fst[rt];i;i=e.nxt[i]){ 35 LCA(e.y[i]); 36 fa[getf(e.y[i])]=rt; 37 } 38 vis[rt]=1; 39 for (int i=q.fst[rt];i;i=q.nxt[i]) 40 if (vis[q.y[i]]&&!ans[q.z[i]]) 41 ans[q.z[i]]=dis[q.y[i]]+dis[rt]-2*dis[getf(q.y[i])]; 42 } 43 int main(){ 44 scanf("%d",&T); 45 while (T--){ 46 q.set(),e.set(); 47 memset(in,0,sizeof in); 48 memset(vis,0,sizeof vis); 49 memset(ans,0,sizeof ans); 50 scanf("%d%d",&n,&m); 51 for (int i=1;i<n;i++) 52 scanf("%d%d%d",&from,&to,&dist),e.add(from,to,dist),in[to]++; 53 for (int i=1;i<=m;i++) 54 scanf("%d%d",&from,&to),q.add(from,to,i),q.add(to,from,i); 55 rt=0; 56 for (int i=1;i<=n&&rt==0;i++) 57 if (in[i]==0) 58 rt=i; 59 dis[rt]=0; 60 dfs(rt); 61 for (int i=1;i<=n;i++) 62 fa[i]=i; 63 LCA(rt); 64 for (int i=1;i<=m;i++) 65 printf("%d\n",ans[i]); 66 } 67 return 0; 68 }
題目二
算法分類:遞推DP,組合數
原題:HDU4489
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 32768/32768 K (Java/Others)
1 c[1][0]=c[1][1]=1; 2 for(int i=2;i<=20;i++){ 3 c[i][0]=c[i][i]=1; 4 for(int j=1;j<i;j++){ 5 c[i][j]=c[i-1][j-1]+c[i-1][j]; 6 } 7 }
咱們能夠用兩個dp數組,dp1和dp2分別表示以「高矮」結尾的方法數和以「矮高」開頭的方法數。因爲對於n個士兵排列的總方法數,以「高矮」和「矮高」開頭的方法數各佔總數的一半,同理以「高矮」結尾的方法數也同樣。因此dp1[n]和dp2[n]都等於ans[n]的一半。
遞推方程爲:
ans[i]=dp1[j]*dp2[i-1-j]*c[i-1][j] (0<=j<i)
固然若是隻用一個dp數組也是同樣,
dp[i]=(dp[j]/2)*(dp[i-1-j]/2)*c[i-1][j] (0<=j<i)
這裏n最大隻有20,因此直接一次性打表把全部答案存下來,避免後面重複計算。注意答案的數值可能很大,因此要用long long。
上代碼:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 ll c[22][22]; 5 ll dp1[25],dp2[25]; 6 #define scan(i) scanf("%d",&i) 7 int main(){ 8 int N;scan(N); 9 c[1][0]=c[1][1]=1; 10 for(int i=2;i<=20;i++){ 11 c[i][0]=c[i][i]=1; 12 for(int j=1;j<i;j++){ 13 c[i][j]=c[i-1][j-1]+c[i-1][j]; 14 } 15 } 16 dp1[1]=dp1[0]=dp2[0]=dp2[1]=1; 17 for(int i=2;i<=20;i++){ 18 ll t=0; 19 for(int j=0;j<i;j++){ 20 t+=dp1[j]*dp2[i-1-j]*c[i-1][j]; 21 } 22 dp1[i]=dp2[i]=t/2; 23 } 24 while(N--){ 25 int x,y; 26 scanf("%d%d",&x,&y); 27 if(y==1){ 28 printf("%d 1\n",x); 29 continue; 30 } 31 ll ans=dp1[y]*2; 32 printf("%d %lld\n",x,ans); 33 } 34 return 0; 35 }
題目三
題目來源:CF#552(div3)
G題:數論
G. Minimum Possible LCM
You are given an array aa consisting of nn integers a1,a2,…,ana1,a2,…,an .
Your problem is to find such pair of indices i,ji,j (1≤i<j≤n1≤i<j≤n ) that lcm(ai,aj)lcm(ai,aj) is minimum possible.
lcm(x,y)lcm(x,y) is the least common multiple of xx and yy (minimum positive number such that both xx and yy are divisors of this number).
The first line of the input contains one integer nn (2≤n≤1062≤n≤106 ) — the number of elements in aa .
The second line of the input contains nn integers a1,a2,…,ana1,a2,…,an (1≤ai≤1071≤ai≤107 ), where aiai is the ii -th element of aa .
Print two integers ii and jj (1≤i<j≤n1≤i<j≤n ) such that the value of lcm(ai,aj)lcm(ai,aj) is minimum among all valid pairs i,ji,j . If there are multiple answers, you can print any.
5
2 4 8 3 6
1 2
5
5 2 11 3 7
2 4
6
2 5 10 1 10 2
1 4
題意:
給出n個數,要求輸出最小公倍數最大的兩個數的下標
知識補充:GCD和LCM:
GCD即最大公約數,求最大公約數有多種方法,常見的有質因數分解法、短除法、展轉相除法、更相減損法。
algorithm庫裏有函數__gcd()
歐幾里得(展轉相除法):
1 int gcd(int a,int b) 2 3 { 4 5 if(b==0) return a; 6 7 return gcd(b,a%b); 8 9 } 10 11 12 13 14 int gcd(int x,int y){return y==0?x:GCD(x%y)}
更相減損術:
1 while(!(a%2) && !(b%2)) 2 { 3 a = a/2; 4 b = b/2; 5 } 6 while(a != b) 7 { 8 if(a>b){ 9 a = a-b; 10 }else{ 11 b = b-a; 12 } 13 }
stein算法:Stein算法只有整數的移位和加減法。原理以下:
1 int SteinGCD(int a, int b) { 2 if (a < b) { int t = a; a = b; b = t; } 3 if (b == 0) return a; 4 if ((a & 1) == 0 && (b & 1) == 0) 5 return SteinGCD(a >> 1, b >> 1) << 1; 6 else if ((a & 1) == 0 && (b & 1) != 0) 7 return SteinGCD(a >> 1, b); 8 else if ((a & 1) != 0 && (b & 1) == 0) 9 return SteinGCD(a, b >> 1); 10 else 11 return SteinGCD(a - b, b); 12 }
LCM即最小公倍數
有以下公式 LCM(a,b)*GCD(a,b)=a*b;經過分解分解質因數能夠很容易的證實。
1 int GCD(int x,int y){ 2 3 return !y?x:GCD(y,x%y); 4 5 } 6 7 int LCM(int x,int y){ 8 9 return x*y/GCD(x,y); 10 11 }
題解:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn=1e7+10; 5 int n,v; 6 int pos[maxn]; 7 ll ans; 8 int ai,aj; 9 int main(){ 10 scanf("%d",&n); 11 ans=1e18; 12 for(int i=1;i<=n;i++){ 13 scanf("%d",&v); 14 if(pos[v]&&ans>v) 15 ans=v,ai=pos[v],aj=i; 16 pos[v]=i; 17 } 18 for(ll i=1;i<maxn;i++) 19 { 20 ll first=-1; 21 for(ll j=i;j<maxn;j+=i) 22 { 23 if(pos[j]) 24 { 25 if(first==-1)first=j; 26 else 27 { 28 if(ans>first*j/i) 29 ans=first*j/i,ai=pos[first],aj=pos[j]; 30 break; 31 } 32 } 33 } 34 } 35 if(ai>aj){ 36 int temp=ai; 37 ai=aj; 38 aj=temp; 39 } 40 printf("%d %d\n",ai,aj); 41 return 0; 42 }
E題:
There are nn students standing in a row. Two coaches are forming two teams — the first coach chooses the first team and the second coach chooses the second team.
The ii -th student has integer programming skill aiai . All programming skills are distinct and between 11 and nn , inclusive.
Firstly, the first coach will choose the student with maximum programming skill among all students not taken into any team, and kk closest students to the left of him and kk closest students to the right of him (if there are less than kk students to the left or to the right, all of them will be chosen). All students that are chosen leave the row and join the first team. Secondly, the second coach will make the same move (but all students chosen by him join the second team). Then again the first coach will make such move, and so on. This repeats until the row becomes empty (i. e. the process ends when each student becomes to some team).
Your problem is to determine which students will be taken into the first team and which students will be taken into the second team.
The first line of the input contains two integers nn and kk (1≤k≤n≤2⋅1051≤k≤n≤2⋅105 ) — the number of students and the value determining the range of chosen students during each move, respectively.
The second line of the input contains nn integers a1,a2,…,ana1,a2,…,an (1≤ai≤n1≤ai≤n ), where aiai is the programming skill of the ii -th student. It is guaranteed that all programming skills are distinct.
Print a string of nn characters; ii -th character should be 1 if ii -th student joins the first team, or 2 otherwise.
5 2 2 4 5 3 1
11111
5 1 2 1 3 5 4
22111
7 1 7 2 1 3 5 4 6
1121122
5 1 2 4 5 3 1
21112
In the first example the first coach chooses the student on a position 33 , and the row becomes empty (all students join the first team).
In the second example the first coach chooses the student on position 44 , and the row becomes [2,1][2,1] (students with programming skills [3,4,5][3,4,5] join the first team). Then the second coach chooses the student on position 11 , and the row becomes empty (and students with programming skills [1,2][1,2] join the second team).
In the third example the first coach chooses the student on position 11 , and the row becomes [1,3,5,4,6][1,3,5,4,6] (students with programming skills [2,7][2,7] join the first team). Then the second coach chooses the student on position 55 , and the row becomes [1,3,5][1,3,5] (students with programming skills [4,6][4,6] join the second team). Then the first coach chooses the student on position 33 , and the row becomes [1][1] (students with programming skills [3,5][3,5] join the first team). And then the second coach chooses the remaining student (and the student with programming skill 11 joins the second team).
In the fourth example the first coach chooses the student on position 33 , and the row becomes [2,1][2,1] (students with programming skills [3,4,5][3,4,5] join the first team). Then the second coach chooses the student on position 11 , and the row becomes empty (and students with programming skills [1,2][1,2] join the second team).
題意:
現有n我的排成一隊,兩個教練輪流從隊中挑選最高的人以及最高的人左邊k我的和右邊k我的,如今須要你求出每一個人分別被哪個教練挑選走。
題解:
這條題目其實就是一條模擬題,惟一要說的就是如何快速找到當前隊中存在的最大值,以及最大值所對應的位置。
用一個check數組保存每個值的狀態,這樣在尋找最大值的時候,能夠從n開始,若是這個值已經被用過了,就減一再判斷,直到找到當前未用的最大值。而後用position數組保存每個值的下標,方便索引。
代碼:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn=2e5+5; 4 int n,k,x; 5 struct node{ 6 int val; 7 int id; 8 node *pre; 9 node *next; 10 }; 11 node p[maxn]; 12 int ans[maxn]; 13 int position[maxn]; 14 bool check[maxn]; 15 inline void build(){ 16 for(int i=1;i<=n;i++){ 17 scanf("%d",&x); 18 p[i].val=x; 19 p[i].id=i; 20 p[i].pre=&p[i-1];p[i].next=&p[i+1]; 21 if(i==1)p[i].pre=NULL; 22 if(i==n)p[i].next=NULL; 23 position[x]=i; 24 } 25 return; 26 } 27 int main(){ 28 scanf("%d%d",&n,&k); 29 build(); 30 int ma=n; 31 int an=1; 32 while(ma!=0){ 33 check[ma]=true; 34 ans[position[ma]]=an; 35 int po=position[ma]; 36 int k1=k;int k2=k; 37 int p1=po,p2=po; 38 while(k1--){ 39 if(p[po].next==NULL)break; 40 int v=p[po].next->val; 41 check[v]=true; 42 ans[position[v]]=an; 43 p1=p[po].next->id; 44 p[po].next=p[po].next->next; 45 } 46 while(k2--){ 47 if(p[po].pre==NULL)break; 48 int v=p[po].pre->val; 49 check[v]=true; 50 ans[position[v]]=an; 51 p2=p[po].pre->id; 52 p[po].pre=p[po].pre->pre; 53 } 54 if(p[p1].next!=NULL)p1=p[p1].next->id; 55 else p1=n+1; 56 if(p[p2].pre!=NULL)p2=p[p2].pre->id; 57 else p2=0; 58 if(p1<=n&&p2>=1){ 59 p[p1].pre=&p[p2]; 60 p[p2].next=&p[p1]; 61 } 62 if(p1<=n&&p2<1)p[p1].pre=NULL; 63 if(p1>n&&p2>=1)p[p2].next=NULL; 64 if(an==1)an=2;else an=1; 65 while(check[ma])ma--; 66 } 67 for(int i=1;i<=n;i++) 68 printf("%d",ans[i]); 69 return 0; 70 }