2017.7.7 C组总结
来源:互联网 发布:debian安装软件命令 编辑:程序博客网 时间:2024/06/08 11:47
NO.1
题目描述:
给定一个长度为N的序列a,对于每一个数都可选或不选,把选出的数有序组成一个新的序列b,使b序列的“和谐数”最大。
一个序列的和谐数如下定义:对于位置i,如果第奇数次选的则加上bi,偶数次选的则减去bi
注意:新的序列b必须是从左到右依次在a序列选择的,即不能打乱顺序。
思路:DP
设f[i,0/1]表示第i个位置选(为0)或不选(为1)的“和谐数”最大
状态转移方程为:
f[i,0]=max(f[i-1,0],f[i-1,1]+a[i])
f[i,1]=max(f[i-1,1],f[i-1,0]-a[i])
用了DP,妈妈再也不用担心我不会AC了
代码:
uses math;var n,i,x:longint; f:array[0..10000001,0..1]of int64;begin assign(input,'a.in'); assign(output,'a.out'); reset(input); rewrite(output); readln(n); for i:=1 to n do begin read(x); f[i,1]:=max(f[i-1,1],f[i-1,0]-x); f[i,0]:=max(f[i-1,0],f[i-1,1]+x); end; write(max(f[n,1],f[n,0])); close(input); close(output);end.
NO.2
题目描述:
ProKing来到了家里,忽然发现桌台上有n1个玩偶,每个玩偶上有一个对应的值,而在桌台下也有n2个玩偶,每个玩偶上也有一个对应的值,现在,proking想知道台上的玩偶的默契值除以台下玩偶的默契值得到的既约分数(既约分数就是分子分母最大公约数为1的分数)
默契值定义为所有玩偶的权值的乘积.
20%思路:暴力
直接将两个序列乘起来,再用辗转相除法求最大公因数
60%思路:高精度
可以使用高精度,把两个序列两两去除公因数,时间复杂度O(n^2)
100%思路:高精度+筛素数+压位
先用zs[i]来存i的最小质因数
然后我们对每个序列开一个数组s[i,j]为第j序列拥有i质因数的个数,那么循环枚举a[i,j]的最小质因数(zs[a[i,j]),求出除完最小质数后的数(a[i,j]-a[i,j] div zs[a[i,j]]),再求除后的数的最小质因数……
到这里,我们求出了每个序列分解质因数后的每个质因数所拥有的质数,那么就去重!!!
去重分三种情况:
①s[i,1]>s[i,2](也就是第一个序列拥有i的数量比第二个序列多),那么s[i,1]-=s[i,2] s[i,2]=0
②s[i,2]>s[i,1](第一个序列拥有i的数量比第二个序列少),那么s[i,2]-=s[i,1] s[i,1]=0
③s[i,1]=s[i,2](第一个序列拥有i的数量等于第二个序列拥有i的数量),那么s[i,1]=s[i,2]=0
这样做完,两个序列就必须是互质的
然后,就用高精度乘法
因为只用高精度乘法,是会超时的,所以就要用压位
所谓压位,就是将原本存一位的a[i],改成存多位
那么压位很简单实现
还有一个细节,就是在压位输出时,一般会将首位的0省略不输,那么就要加一个处理,将0位补齐(Tips:第一位不用补0)
代码:
const max=2300; mo=100000000;var n:array[1..2]of int64; a,s,l:array[0..100101,1..2]of int64; x,z,y:int64; i,j,k:longint; zs:array[0..12624]of int64;procedure zxs;var i,x:longint;begin for i:=2 to 10000 do begin x:=0; for j:=2 to trunc(sqrt(i)) do if (i mod j=0) then begin zs[i]:=j; x:=j; break; end; if x=0 then zs[i]:=i; end;end;begin assign(input,'count.in'); assign(output,'count.out'); reset(input); rewrite(output); read(n[1]); for i:=1 to n[1] do read(a[i,1]); readln; read(n[2]); for i:=1 to n[2] do read(a[i,2]); zxs; for i:=1 to 2 do for j:=1 to n[i] do begin x:=a[j,i]; while x>1 do begin inc(s[zs[x],i]); x:=x div zs[x]; end; end; for i:=2 to 10000 do if (s[i,1]>0)and(s[i,2]>0) then if (s[i,1]>s[i,2]) then begin s[i,1]:=s[i,1]-s[i,2]; s[i,2]:=0; end else begin s[i,2]:=s[i,2]-s[i,1]; s[i,1]:=0; end; l[max,1]:=1; l[max,2]:=1; for i:=1 to 2 do for j:=2 to 10000 do begin z:=s[j,i]; while z>0 do begin dec(z); for k:=2 to max do l[k,i]:=l[k,i]*j; for k:=max downto 2 do if l[k,i]>9 then begin l[k-1,i]:=l[k-1,i]+l[k,i] div mo; l[k,i]:=l[k,i] mod mo; end; end; end; x:=0; for i:=1 to 2 do begin for j:=0 to max do if l[j,i]>0 then begin x:=j; break; end; write(l[x,i]); for j:=x+1 to max do begin y:=l[j,i]; z:=0; if y=0 then y:=1; while y<mo do begin y:=y*10; z:=z+1; end; for k:=2 to z do write('0'); write(l[j,i]); end; write(' '); end; close(input); close(output);end.
NO.3
题目描述:
给定一个长度为n的序列a,试求出对于序列a的每一个前缀的终极数x,使得
最小,试求出终极数t(如若有多个终极数t,只需输出最小的那个)
思路:堆维护+快排
这题的主要思路就是就中位数
堆有两个堆一个是大根堆,一个是小根堆,其实小根堆的堆顶就是中位数
这个问题就转移为维护一个堆
维护一个堆有三种情况:
①将大根堆从大到小排序
②将小根堆从小到大排序
③如果,大根堆顶大于小根堆顶,就将大根堆顶和小根堆顶交换,交换后的大根堆和小根堆都要进行重新排序
那么每一次的小根堆顶就是x终极数
最后再快排终极数,求出中位数即可AC
代码:
#include<cstdio>#include<iostream>#include<cstdlib>using namespace std;int x1,x2,a[1000001]={0},big[1000001]={0},small[1000001],x[1000001],i,t,n;void temp1(int x){ while ((x>1)&&(small[x]<small[x/2])) { t=small[x];small[x]=small[x/2];small[x/2]=t; x=x/2; }}void temp2(int x){ while ((x>1)&&(big[x]>big[x/2])) { t=big[x];big[x]=big[x/2];big[x/2]=t; x=x/2; }}void doit1(int x){ int y; while (((x*2<=x1)&&(small[x*2]<small[x]))||(((x*2+1)<=x1)&&(small[x*2+1]<small[x]))) { y=x*2; if ((x*2+1<=x1)&&small[x*2+1]<small[x*2]) y=x*2+1; t=small[y];small[y]=small[x];small[x]=t; x=y; }}void doit2(int x){ int y; while (((x*2<=x2)&&(big[x*2]>big[x]))||(((x*2+1)<=x2)&&(big[x*2+1]>big[x]))) { y=x*2; if (x*2+1<=x2&&big[x*2+1]>big[x*2]) y=x*2+1; t=big[y];big[y]=big[x];big[x]=t; x=y; }}void qsort(int l,int r){ int i=l,j=r,mid=x[(l+r)/2]; if (l>=r) return; do { while(x[i]<mid) i++; while(x[j]>mid) j--; if (i<=j) { t=x[i];x[i]=x[j];x[j]=t; i++;j--; } } while (i<=j); qsort(l,j); qsort(i,r);}int main(){ freopen("c.in","r",stdin); freopen("c.out","w",stdout); scanf("%d",&n); x1=0; x2=0; for (int i=1;i<=n;i++) { scanf("%d",&a[i]); if (i%2==1) { x1++; small[x1]=a[i]; temp1(x1); if (small[1]<big[1]) { t=small[1];small[1]=big[1];big[1]=t; doit1(1); doit2(1); } } else { x2++; big[x2]=a[i]; temp2(x2); if (small[1]<big[1]) { t=small[1];small[1]=big[1];big[1]=t; doit1(1); doit2(1); } } x[i]=small[1]; } qsort(1,n); printf("%d",x[n/2]); fclose(stdin); fclose(stdout); return 0;}
NO.4
题目描述:给定一个0-1串,请找到一个尽可能长的子串,其中包含的0与1的个数相等。
思路:前缀和+贪心
如果设遇’0’+1,遇’1’-1
那么如果在前后找到有相等的数值时,这两个中间的序列必定是符合的!(长度就是x[i]-x[j])
那么为了减少时间复杂度,就可以用贪心思想,记录出现这个数的最前和最后
循环一遍求出最长的长度
代码:
var s:ansistring; i,ans,j,min,max,x:longint; min1,max1:array[-1000001..1000001]of longint;begin assign(input,'string.in'); reset(input); assign(output,'string.out'); rewrite(output); fillchar(min,sizeof(min1),127); min1[0]:=0; readln(s); min:=maxlongint; x:=0; max:=0; for i:=1 to length(s) do if s[i]='1' then begin inc(x); if x>max then max:=x; if x<min then min:=x; if i<min1[x] then min1[x]:=i; if i>max1[x] then max1[x]:=i; end else begin dec(x); if x>max then max:=x; if x<min then min:=x; if i<min1[x] then min1[x]:=i; if i>max1[x] then max1[x]:=i; end; for i:=min to max do if ans<max1[i]-min1[i] then ans:=max1[i]-min1[i]; write(ans); close(input); close(output);end.
- 2017.7.7 C组总结
- 2017.7.6 C组总结
- 2017.7.8 C组总结
- 2017.7.9 C组 总结
- 2017.7.11 C组总结
- 2017.7.10 C组总结
- 2017.7.12 C组总结
- 2017.7.13 C组总结
- 2017.7.14 C组总结
- 2017.7.15 C组总结
- C/C++_lesson1~7_总结
- 16.7.16 C组总结
- C组7.16题解&总结
- 16.7.17 C组总结
- 16.8.11 C组总结
- 16.8.12 C组总结
- 16.8.13 C组总结
- 16.8.14 C组总结
- 从尾到头打印链表
- 3611: [Heoi2014]大工程
- windows操作系统之间的时间精确同步
- PAT b1001-1005题解
- C++中内存分配方式、空指针及野指针的区别
- 2017.7.7 C组总结
- 通过Lua代码创建一个Cube,并实现控制行走
- 补间动画
- Liferay DXP Soy porlet系列(四)使用Promise对象访问Liferay web service
- android广告自动设置时间跳界面
- 重建二叉树
- eclipse运行慢经常卡死解决方法
- 弹出对话框
- BZOJ 3012: [Usaco2012 Dec]First! 字典树 拓扑排序