過了6題
我開局秒了1001,隊友讀題1005,我也秒了,幫忙想了一部分1008,寫了1009,想了一點點1010,想了一部分1006(但隊友已經打了,太猛了
1001
題意:求n%(1~n)的或和
n<=10^12
題解:
當模數是n/2以上時,0~n/2-1都會出現一次,當模數是n/2以下時,結果也不超過n/2-1
所以答案就是模出來的最大結果的二進制補全。
代碼:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int main() 4 { 5 int t; 6 scanf("%d",&t); 7 while (t) 8 { 9 t--; 10 long long n; 11 scanf("%lld",&n); 12 if (n&1) n=(n-1)/2;else 13 n=n/2-1; 14 long long pty=1; 15 while (pty<=n) pty*=2; 16 printf("%lld\n",pty-1); 17 } 18 }
1005
題意:
n-1個點,編號從2開始,邊權是兩點編號的LCM,求生成樹
n<=10^7
題解:
如果i是合數,找個因子連邊,新增答案就是i
如果是質數,只能找2或者自己的2倍連邊,新增答案是2*i
預處理即可
代碼:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int zs[5000000],p[10000010]; 4 long long f[10000010]; 5 int main() 6 { 7 int sk=0; 8 for (int i=2;i<=10000000;i++) 9 { 10 if (!p[i]) zs[++sk]=i; 11 for (int j=1;j<=sk&&i*zs[j]<=10000000;j++) 12 { 13 p[i*zs[j]]=1; 14 if (i%zs[j]==0) break; 15 } 16 } 17 f[2]=0; 18 for (int i=3;i<=10000000;i++) if (p[i]) f[i]=f[i-1]+i;else f[i]=f[i-1]+2*i; 19 int t; 20 scanf("%d",&t); 21 while (t) 22 { 23 t--; 24 int n; 25 scanf("%d",&n); 26 printf("%lld\n",f[n]); 27 } 28 }
1008
題意:
一個n*m的矩陣,要找一個最大的子矩陣滿足每列不下降
n,m<=2000
題解:
設p[i][j]表示從(i,j)出發最長的不下降長度
再枚舉行,用單調棧做類似最大矩陣就可以了
代碼:
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define N 2050 4 #define MIN 0xc0c0c0c0 5 using namespace std; 6 int a[2050][2050],p[2050][2050],ans = MIN,maxx = 0; 7 8 void ww() 9 { 10 ans = 0; 11 int n,m; 12 scanf("%d%d",&n,&m); 13 for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&a[i][j]),p[i][j]=1; 14 for (int i=n-1;i>=1;i--) for (int j=1;j<=m;j++) if (a[i][j]<=a[i+1][j]) p[i][j]=p[i+1][j]+1; 15 16 // for (int i=1;i<=n;i++) 17 // { 18 // cout<<endl; 19 // for (int j=1;j<=m;j++) 20 // { 21 // cout<<p[i][j]<<" "; 22 // } 23 // } 24 // cout<<endl; 25 26 int h[N],l[N],r[N],q[N]; 27 for(int z=1;z<=n;z++) 28 { 29 maxx = 0; 30 int tt = 0; 31 q[0] = 0; 32 p[z][0] = p[z][m+1] = -1; 33 for(int i=1;i<=m;i++) 34 { 35 while( p[z][i] <= p[z][ q[tt] ] ) tt--; 36 l[ i ] = q[tt]; 37 q[ ++tt ] = i; 38 } 39 tt = 0; 40 q[0] = m+1; 41 for(int i=m;i>=1;i--) 42 { 43 while( p[z][i] <= p[z][ q[tt] ] ) tt--; 44 r[i] = q[tt]; 45 q[ ++tt ] = i; 46 } 47 for(int i=1;i<=m;i++) 48 { 49 maxx = max( maxx , p[z][i]*( r[i]-l[i]-1 ) ); 50 } 51 ans = max( maxx , ans ); 52 } 53 printf("%d\n",ans); 54 55 } 56 int main() 57 { 58 //freopen("in.txt","r",stdin); 59 int t; 60 scanf("%d",&t); 61 while (t) 62 { 63 t--; 64 65 ww(); 66 } 67 }
1009
題意:
一個n個點的無向帶權聯通圖,在給定數D和K的情況下,如果滿足以下條件:
1.點被分為非空的k組
2.同一組內的兩點之間存在至少一條路徑,路徑上的最大權值小於等於D
3.不同組內的兩點不存在任何一條路徑,路徑上的最大權值小於等於D
這樣的圖被稱為KD圖
給出n和K,求最小的D使得圖是KD圖
n<=10^5
題解:
枚舉答案D,刪去所有大於D的邊,剩余圖聯通塊如果有恰好k個,則D是一個合法答案。
發現這個過程其實就是克魯斯卡爾求最小生成樹的過程,只不過相同邊權需一次加入,再判斷聯通塊的個數與k的關系
代碼:
1 #include<bits/stdc++.h> 2 using namespace std; 3 struct aa 4 { 5 int x,y,z; 6 }e[500050]; 7 bool cmp(aa x,aa y) 8 { 9 return x.z<y.z; 10 } 11 int f[100010]; 12 int get(int x) 13 { 14 if (f[x]==x) return x; 15 return f[x]=get(f[x]); 16 } 17 void ww() 18 { 19 int n,m,k; 20 scanf("%d%d%d",&n,&m,&k); 21 for (int i=1;i<=m;i++) scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].z); 22 sort(e+1,e+m+1,cmp); 23 for (int i=1;i<=n;i++) f[i]=i; 24 e[m+1].z=0; 25 if (n==k) 26 { 27 printf("0\n"); 28 return; 29 } 30 for (int i=1;i<=m;i++) 31 { 32 int xx=get(e[i].x),yy=get(e[i].y); 33 if (xx!=yy) n--; 34 f[xx]=yy; 35 if (e[i].z!=e[i+1].z&&n==k) 36 { 37 printf("%d\n",e[i].z); 38 return; 39 } 40 } 41 printf("-1\n"); 42 } 43 int main() 44 { 45 int t; 46 scanf("%d",&t); 47 while (t) 48 { 49 t--; 50 ww(); 51 } 52 }