夜未央Test1题解
T1 积木游戏
树状数组的一个简单应用,建立一个维护左节点的树状数组和一个维护右节点的树状数组,对于add操作,只要在维护左节点的树状数组l处加1,维护右节点的树状数组r处加1,那么询问[l,r]的答案就是左节点数组的r前缀和减去右节点数组的l-1前缀和。
var q,suml,sumr,i,j,k,l,r,m,n:longint; cl,cr:array[0..60005] of longint; {file} procedure openf; begin assign(input,'block.in'); reset(input); assign(output,'block.out'); rewrite(output); end; procedure closef; begin close(input); close(output); halt; end; {lowbit} function lowbit(p:longint):longint; begin exit(p and -p); end; {add} procedure addl(x,num:longint); begin while x<=n+1 do begin inc(cl[x],num); x:=x+lowbit(x); end; end; procedure addr(x,num:longint); begin while x<=n+1 do begin inc(cr[x],num); x:=x+lowbit(x); end; end; {get} procedure getsuml(x:longint); begin suml:=0; while x>0 do begin inc(suml,cl[x]); x:=x-lowbit(x); end; end; procedure getsumr(x:longint); begin sumr:=0; while x>0 do begin inc(sumr,cr[x]); x:=x-lowbit(x); end; end; begin {input} openf; readln(n,m); {doit} for i:=1 to m do begin readln(q,l,r); if q=1 then begin addl(l,1); addr(r,1); end; if q=2 then begin getsuml(r); getsumr(l-1); writeln(suml-sumr); end; end; closef; end.
#include<iostream> #include<cstdio> #define lowbit(x) x&(-x) using namespace std; int q,i,j,k,l,r,m,n; int cl[60005],cr[60005]; //file void openf() { freopen("block.in","r",stdin); freopen("block.out","w",stdout); } void closef() { fclose(stdin); fclose(stdout); } //INT int INT() { int res; char ch; while (ch = getchar(), !isdigit(ch)); for (res = ch - '0'; ch = getchar(), isdigit(ch);) res = res * 10 + ch - '0'; return res; } //add void addl(int x,int num) { for (; x<=n+1; x+=lowbit(x)) cl[x]+=num; } void addr(int x,int num) { for (; x<=n+1; x+=lowbit(x)) cr[x]+=num; } //get int suml(int x) { int sum = 0; for (; x; x-=lowbit(x)) sum+=cl[x]; return sum; } int sumr(int x) { int sum = 0; for (; x; x-=lowbit(x)) sum+=cr[x]; return sum; } int main() { //input openf(); n = INT(); m = INT(); //doit for (i = 1; i<=m; i++) { q = INT(); l = INT(); r = INT(); if (q==1) { addl(l,1); addr(r,1); }else printf("%d\n",suml(r)-sumr(l-1)); } closef(); return 0; }
T2 数字游戏
这道题首先要做到的就是如何确定方程,由于按照顺序输出,所以我们假定x1<=x2<=x3<=……<=xn,那么我们先将a排序,由于x1+x2是最小的,所以就是a1,同理x1+x3第二小,为a2,但是需要注意的是x2+x3不一定是第三小的,有可能x1+xk比其小,所以我们从a3开始,一个一个假定为x2+x3,联立之前的几个方程,解出x1,x2,x3,然后如果是负数或是无解则跳过,若解出,将x1+x2,x1+x3,x2+x3的结果从a数组中去掉,然后剩下最小的就一定是x1+x4,于是解出x4,之后如上去掉x4与之前几个解的和,剩下最小的是x1+x5,依次做下去,如果其中一个解为负,则该情况不成立,直到所有解解出,输出即可。
用样例小小解释一下,a数组排序后为{1,2,3,4,5,6},则x1+x2=1,x1+x3=2,假设x2+x3=3解得x1=0,所以x2=1,x3=2,去掉1,2,3,集合为{4,5,6},则x4+x1为4,x4=4,去除4,5,6,集合为空,所以解成立,输出0,1,2,4。
var bo:boolean; q,i,j,k,l,m,n,tot:longint; b,a:array[0..5001] of longint; x:array[0..100005] of longint; {file} procedure openf; begin assign(input,'math.in'); reset(input); assign(output,'math.out'); rewrite(output); end; procedure closef; begin close(input); close(output); halt; end; {sort} procedure qsort(l,r:longint); var i,j,mid,t:longint; begin i:=l; j:=r; mid:=b[l+random(r-l+1)]; repeat while b[i]<mid do inc(i); while b[j]>mid do dec(j); if i<=j then begin t:=b[i]; b[i]:=b[j]; b[j]:=t; inc(i); dec(j); end; until i>j; if i<r then qsort(i,r); if l<j then qsort(l,j); end; begin {input} openf; readln(n); n:=n*(n-1) div 2; for i:=1 to n do read(b[i]); randomize; qsort(1,n); {doit} for i:=3 to n do begin bo:=true; move(b[1],a[1],n*sizeof(b[1])); x[1]:=(a[1]+a[2]-a[i]); if x[1]<0 then continue; if x[1] mod 2=0 then begin x[1]:=x[1] div 2; x[2]:=a[1]-x[1]; if x[2]<0 then continue; x[3]:=a[i]-x[2]; if x[3]<0 then continue; end else continue; a[1]:=0; a[2]:=0; a[i]:=0; tot:=3; for j:=3 to n do if a[j]<>0 then begin inc(tot); x[tot]:=a[j]-x[1]; if x[tot]<0 then begin bo:=false; break; end; k:=1; for l:=j to n do if a[l]=x[tot]+x[k] then begin a[l]:=0; inc(k); if k=tot then break; end; if k<>tot then bo:=false; end; if bo then begin for l:=1 to tot do write(x[l],' '); closef; end; end; {closef} writeln('No solution'); closef; end.
#include <iostream> using namespace std; bool bo; int q,i,j,k,l,m,n,tot; int b[5001],a[5001]; int x[100005]; //file void openf() { freopen("math.in","r",stdin); freopen("math.out","w",stdout); } void closef() { fclose(stdin); fclose(stdout); } //INT int INT() { int res; char ch; while (ch = getchar(), !isdigit(ch)); for (res = ch - '0'; ch = getchar(), isdigit(ch);) res = res * 10 + ch - '0'; return res; } //sort void qsort(int l,int r) { int i,j,t,mid; mid = b[(l+r)>>1]; i = l; j = r; do { while (b[i]<mid) i++; while (b[j]>mid) j--; if (i<=j) { t = b[i]; b[i] = b[j]; b[j] = t; i++; j--; } } while (i<=j); if (i<r) qsort(i,r); if (l<j) qsort(l,j); } int main() { int step = 0; //input openf(); n = INT(); n = n * (n - 1) / 2; if (n==1) { cout<<"No solution"; closef(); return(0); } for (i=1; i<=n; i++) b[i] = INT(); qsort(1,n); //doit for (i=3; i<=n; i++) { bo = true; for (j=1; j<=n; j++) a[j] = b[j]; x[1] = (a[1]+a[2]-a[i]); if (x[1]<0) continue; if (x[1]%2==0) { x[1] /= 2; x[2] = a[1]-x[1]; if (x[2]<0) continue; x[3] = a[i]-x[2]; if (x[3]<0) continue; } else continue; a[1] = 0; a[2] = 0; a[i] = 0; tot = 3; for (j=3; j<=n; j++) if (a[j]!=0) { x[++tot] = a[j]-x[1]; if (x[tot]<0) { bo = false; break; } k = 1; for (l=j; l<=n; l++) if (a[l] == x[tot]+x[k]) { a[l] = 0; k++; if (k == tot) break; } if (k!=tot) bo = false; } if (bo) { for (l=1; l<=tot; l++) printf("%d ",x[l]); //system("pause"); closef; return 0; } } cout<<"No solution"; closef(); return 0; }
T3 造梦
分析题目,可得一个很显然的贪心,所有石柱相差不超过5那么全部一样是最优的,因为最终高度取决于最短的石柱,然后如何去求这个统一高度呢?我们可以这样设想,如果知道了这个需要的高度,那么验证是否可以达到是不是就非常简单了,只要将每一块石料除以已知高度加到ans上,看是否达到需要的块数就可以了。那么,二分检索的模型就浮出水面了,我们取最长石料为右端点,1为左端点,每一次取中点检验是否可行,然后根据单调性更新左右端点,就可以简单地完成这道题了。
var l,r,ans,mid,cnt,i,j,k,m,n:longint; a:array[0..1000005] of longint; {file} procedure openf; begin assign(input,'build.in'); reset(input); assign(output,'build.out'); rewrite(output); end; procedure closef; begin close(input); close(output); halt; end; {check} function check(x:longint):boolean; begin cnt:=0; for i:=1 to m do begin inc(cnt,a[i] div x); if cnt>=n then exit(true); end; exit(false); end; begin {input} openf; readln(m,n); {doit} for i:=1 to m do begin read(a[i]); if a[i]>r then r:=a[i]; end; l:=1; repeat mid:=(l+r)>>1; if check(mid) then begin ans:=mid; l:=mid+1; end else r:=mid-1; until l>r; {output} writeln(ans); closef; end.
#include <iostream> #include <cstdio> using namespace std; const int size=1000005; int l,r,ans,mid,cnt,i,j,k,m,n; int a[size]; //file void openf() { freopen("build.in", "r", stdin); freopen("build.out", "w", stdout); } void closef() { fclose(stdin); fclose(stdout); } //check bool check(int x) { cnt = 0; for (i = 0; i < m; i++) { cnt+=(a[i] / x); if (cnt >= n) return(true); } return(false); } //INT int INT() { int res; char ch; while (ch = getchar(), !isdigit(ch)); for (res = ch - '0'; ch = getchar(), isdigit(ch);) res = res * 10 + ch - '0'; return res; } //main int main() { openf(); m = INT(); n = INT(); for (i = 0; i < m; i++) { a[i] = INT(); if (a[i] > r) r = a[i]; } l = 1; do { mid = (l + r) >> 1; if (check(mid)) { ans = mid; l = mid + 1; } else r = mid-1; } while (l <= r); printf("%d",ans); //system("pause"); closef; return(0); }
愿你出走半生,归来仍是少年