过了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 }