1001 Mod, Or and Everything
题意:给你一个n,计算(n mod 1)or (n mod 2) or...or(n mod (n-1)) or (n mod n)。
思路:我们可以发现余数以此为0,1,2,3,...,(n-1)/2,...,3,2,1,0。即包含了0~(n-1)/2中的所有数,由此可以推得答案为,其中是第一个大于(n-1)/2的二次幂。
代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int t; cin >> t; while(t--) { ll n; cin >> n; ll tmp=(n-1)/2; ll ans = 1; while(ans <= tmp) { ans*=2; } cout << ans - 1 << endl; } return 0; }
1005 Minimum spanning tree
题意:一个图中有n-1个点,每个点的权值为2~n,在顶点a和顶点之间连一条边的代价是lcm(a,b),计算将所有点连成一个树的最小代价。
思路:所有质数都连接到2上来,所有合数都连接到它的最小质因数上。
代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 10000000+5; ll sum[N]; ll st[N],prime[N],cnt; ll vis[N]; void ola() { for(int i=2;i<=10000000;i++) { if(!st[i]) prime[++cnt]=i; for(int j=1;j<=cnt&&i*prime[j]<=10000000;j++) { st[i*prime[j]]=1; vis[i*prime[j]]=i; //vis[i*prime[j]]=i; //cout << "ceshi : " << i*prime[j] << " " << i << endl; if(i%prime[j]==0) { break; } } } } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int t; cin >> t;ola(); for(int i=3;i<=N;i++) { if(st[i]==0) sum[i]=sum[i-1]+i*2; else sum[i]=sum[i-1]+i; } while(t--) { int n; cin >> n; cout << sum[n] << endl; } return 0; }
1008 Maximal submatrix
题意:给你一个n*m的矩阵,输出每列不递减的最大面积子矩阵的面积。
思路:我们先对每一列的值进行一个处理,如果当前位置的值小于上一个位置的值我们令它为1,否则为上一个+1。例如某一列的值为2,3,5,4,5,6,那么处理完之后应该为1,2,3,1,2,3,这样我们就得到了这一列到该元素的一个不递减长度。最后循环每行,判断一下连续个数。注意不要忘记将算出来的答案与m相比。
代码:
#include <bits/stdc++.h> using namespace std; const int N = 2e3+5; int a[N][N],h[N][N]; int main() { int t; scanf("%d",&t); while(t--) { int n,m; scanf("%d %d",&n,&m); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { scanf("%d",&a[i][j]); } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(i==1||a[i][j]<a[i-1][j]) { h[i][j]=1; } else h[i][j]=h[i-1][j]+1; } } int cnt=0,minn=n,ans=0; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(h[i][j]!=1) { cnt++; minn=min(minn,h[i][j]); if(j==m&&cnt) { ans=max(ans,minn*cnt); cnt=0; minn=N; } } else { ans=max(ans,minn*cnt); cnt=0; minn=n; } } } printf("%d\n",max(ans,m)); } return 0; }
1009 KD-Graph
题意:n个点,m条边,将n个点严格分为k组。是否存在一个最小的D值恰好使原图变为k个连通的部分。题目中连通的定义:
①对于任意一组,组内的顶点两两之间至少存在一条权值小于等于w的路径
②对于任意两组,组间的顶点两两之间不存在任何一条权值小于等于w的路径
思路:将权值按照从小到大排序,用now记录当前组数,取出相同权值的所有边,将这些边的顶点用并查集两两合并,没合并一次组数now-1,因为每合并一次独立的顶点就少了一个,同时记录此时的权值ans,当枚举到下一阶段时,此时边权与上次不一样,判断此时now的值,如果等于k,则输出此时的ans。
赛后补这道题wa了十多次,结果改用C++,t了,再改scanf,过了。。。跟六月份打CCPC正好反过来,当时有一道题用C++一直wa,改用G++就a了。。。
代码:
#include <iostream> #include <algorithm> using namespace std; const int N = 1e6+10; struct node{ int u,v,c; }p[N]; int fa[N]; bool cmp(node x, node y) { return x.c<y.c; } int get(int x) { return fa[x]==x?fa[x]:fa[x]=get(fa[x]); } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int t; scanf("%d",&t); while(t--) { int n,m,k;scanf("%d %d %d",&n,&m,&k); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++) { scanf("%d %d %d",&p[i].u,&p[i].v,&p[i].c); } sort(p + 1, p + 1 + m, cmp); int now=n,ans=0; bool flag=false; for(int i=1;i<=m;i++) { if(p[i].c!=p[i-1].c) { if(now==k) { break; } } int x=get(p[i].u); int y=get(p[i].v); if(x==y) continue; now--; fa[x]=y; ans=p[i].c; } if(now==k) cout << ans << endl; else cout << -1 << endl; } return 0; }