暑假集训第一弹
A:POJ 4311 Meeting point-1
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4311
题意:给定n个点的坐标,然后让你找出其中一个点到其他所有点的距离和最小,并输出这个距离。
(只能横着走和竖着走)
输入:先输入1个T,然后是T组数组,每组数据 先是一个n 然后n个点。
输出:距离。
思路:
因为直接爆搜是n^2的,数据范围是1e5
所以 先对x排序,然后把每个点与x0的距离存下来,然后再对y排序,同理。
最后扫一遍数组。
// ans=(x1+x2+x3+...+xn)-n*xi+(y1+y2+...+yn)-n*yi;
// 表述能力过差 直接写一个公式,最后不断更新ans即可
代码:
#include<cstdio> #include<cstring> #include<queue> #include<cmath> #include<algorithm> using namespace std; typedef struct{ long long x,y; int k; }point; bool cmpx(point a,point b) { if(a.x==b.x) return a.y<b.y; return a.x<b.x; } bool cmpy(point a,point b) { if(a.y==b.y) return a.x<b.x; return a.y<b.y; } long long xx[100100],yy[100100]; long long sumx[100100],sumy[100100]; point a[100100]; int main() { // freopen("a.txt","r",stdin); int T; scanf("%d",&T); while(T--) { memset(xx,0,sizeof xx); memset(yy,0,sizeof yy); memset(sumx,0,sizeof sumx); memset(sumy,0,sizeof sumy); int n; scanf("%d",&n); for(int i=0;i<n;i++) scanf("%lld %lld",&a[i].x,&a[i].y); sort(a,a+n,cmpx); a[0].k=0; for(int i=1;i<n;i++) { xx[i]=a[i].x-a[0].x; sumx[i]=sumx[i-1]+xx[i]; a[i].k=i; } sort(a,a+n,cmpy); for(int i=1;i<n;i++) { yy[i]=a[i].y-a[0].y; sumy[i]=sumy[i-1]+yy[i]; } long long ans=sumx[n-1]+sumy[n-1]; for(int i=1;i<n;i++) { long long now=0; now += ( a[i].k*xx[a[i].k]-sumx[a[i].k-1] ); // x 左 now += ( sumx[n-1]-(n-a[i].k-1)*xx[a[i].k]-sumx[a[i].k] ); // x 右 now += ( i*yy[i]-sumy[i-1] ); // y 下 now += ( sumy[n-1]-(n-i-1)*yy[i]-sumy[i] ); // y 上 if(now<ans) ans=now; } printf("%lld\n",ans); } }
———————————————————分————割————线———————————————————
B:ACdream 1187 Rational Number Tree
传送门:http://acdream.info/problem?pid=1187
题意:有一棵二叉树根是 1/1,左儿子是 父亲的分子/父亲的分子+分母 右儿子是 父亲的分子+分母/父亲的分母
两个操作 给个位置 问你分数 ; 给个分数 问你位置
输入:一个n 然后n个操作
输出:Case #n:分子 分母(或者位置)
这里的数据会很大 所以要 llu
思路:
简单的二叉树递归。
代码:
#include<cstdio> #include<cstring> #include<queue> #include<cmath> #include<algorithm> using namespace std; int t; unsigned long long pp,qq; void solve1(unsigned long long n) { pp=qq=1; if(n==1) return; int x[100], tot=0; while(n) { x[++tot]=n%2; n=n/2; } for(int i=tot-1;i;i--) { if(x[i]) pp=pp+qq; else qq=pp+qq; } } unsigned long long solve2(unsigned long long p,unsigned long long q) { if(p==1 && q==1) return 1; int dep=0, x[100]; while(p != 1 || q != 1) { dep++; if(p>q) // 右 { p=p-q; x[dep]=1; } else // 左 { q=q-p;; x[dep]=0; } } unsigned long long ans=1; for(int i=dep;i;i--) // 从下往上*2 { ans=ans<<1; if(x[i]) ans++; } return ans; } int main() { int T; scanf("%d",&T); for(t=1;t<=T;t++) { int type; scanf("%d",&type); if(type&1) { unsigned long long n; scanf("%llu",&n); solve1(n); printf("Case #%d: %llu %llu\n",t,pp,qq); } else { unsigned long long x,y; scanf("%llu %llu",&x,&y); printf("Case #%d: %llu\n",t,solve2(x,y)); } } }
———————————————————分————割————线———————————————————
C:并不会做 // 留坑
———————————————————分————割————线———————————————————
D:CodeForces 266C Below the Diagonal
传送门:http://codeforces.com/problemset/problem/266/C
题意:给你一个n*n的矩阵并且其中有n-1个位置是1(其他位置是0)
每次可以交换其中任意两行或者两列,把所有的1都交换到对角线以下(横坐标大于纵坐标)
输出详细操作
输入:一个n,表示矩阵的宽,然后n-1行有n-1个位置表示那里是1
输出:每行一个操作
思路:
类似线代的上三角矩阵。
因为 n-1 个 1 所以 存在某一列没有1,我们把它最后一列,然后找到一个有1的换到最后一行
这样相当于变成了一个新的矩阵(去掉最后一行和最后一列) n-1的宽 n-2的点 循环直到n=0;
代码:
#include<cstdio> #include<cstring> #include<queue> #include<cmath> #include<algorithm> using namespace std; typedef struct{ int p,x,y; }way; way ans[10010]; int tot=0,a[1005][1005]; void solve(int n) { int sh=0; for(int i=1;i<=n;i++) if(a[i][n]) sh=1; if(sh) { for(int i=1;i<n;i++) { int flag=1; for(int j=1;j<=n;j++) if(a[j][i]) flag=0; if(flag) { for(int j=1;j<=n;j++) swap(a[j][i],a[j][n]); ans[tot].p=2; ans[tot].x=i; ans[tot].y=n; tot++; break; } } } sh=1; for(int i=1;i<n;i++) if(a[n][i]) sh=0; if(sh) { for(int i=1;i<n;i++) { int flag=0; for(int j=1;j<n;j++) if(a[i][j]) flag=1; if(flag) { for(int j=1;j<=n;j++) swap(a[i][j],a[n][j]); ans[tot].p=1; ans[tot].x=i; ans[tot].y=n; tot++; break; } } } if(n>1) solve(n-1); } int main() { int n; scanf("%d",&n); memset(a,0,sizeof a); for(int i=1;i<n;i++) { int x,y; scanf("%d %d",&x,&y); a[x][y]=1; } solve(n); printf("%d\n",tot); for(int i=0;i<tot;i++) printf("%d %d %d\n",ans[i].p,ans[i].x,ans[i].y); }
———————————————————分————割————线———————————————————
E:并不会做 // 留坑
———————————————————分————割————线———————————————————
F:CodeForces 680A Bear and Five Cards (水)
传送门:http://codeforces.com/problemset/problem/680/A
题意:给你5个数字,重复的数字可以去掉,但是最多去掉3个且只能去掉一个数字,问你和的最小值。
输入:5个数字
输出:最小的和
思路:
因为数字最大是100 所以开个 数组存每个数字的个数(超过3的定为3)。
然后扫一遍 把重复的最大值maxx保存下来,
输出sum-maxx
代码:
#include<cstdio> #include<cstring> #include<queue> #include<cmath> #include<algorithm> int a[100]; int main() { memset(a,0,sizeof a); int sum=0,isum=0,x; for(int i=1;i<=5;i++) { scanf("%d",&x); a[x]++; if(a[x]>3) a[x]--; sum += x; } for(int i=1;i<=100;i++) if(a[i]>1) isum=a[i]*i<isum?isum:a[i]*i; printf("%d\n",sum-isum); }