【解题报告】13级个人结业赛(一) ——涨姿势专场
A.优化(1)
前缀和优化。
预处理出a数组0~i的和,然后每次取b[i]~i的和时取出sum[i]-sum[b[i]-1]。
1 #include<stdio.h> 2 #include<math.h> 3 #include<string.h> 4 #include<iostream> 5 #include<algorithm> 6 #include<map> 7 #include<set> 8 #include<queue> 9 #include<stack> 10 using namespace std; 11 typedef long long ll; 12 ll f2(ll a[],ll b[],ll len) 13 { 14 ll ans=0,i,j; 15 for(i=0;i<len;i++) 16 { 17 if(i!=0) a[i]+=a[i-1]; 18 if(b[i]!=0) ans+=a[i]-a[b[i]-1]; 19 else ans+=a[i]; 20 } 21 return ans; 22 } 23 ll a[1000005],b[1000005],len; 24 int main() 25 { 26 int i; 27 while(scanf("%lld",&len)!=EOF)//len<1000000 28 { 29 for(i=0;i<len;i++) scanf("%lld",&a[i]);//-1000000000<=a[i]<=1000000000 30 for(i=0;i<len;i++) scanf("%lld",&b[i]);//0<=b[i]<=i 31 printf("%lld\n",f2(a,b,len)); 32 } 33 return 0; 34 }
B.优化(2)
离线处理。读入所有的点和查询,按x坐标排序,然后从小到大维护最大y。碰到点时更新最大y,碰到查询时将查询结果保存起来。处理完所有查询后按原来输入的顺序输出答案。
1 #include<stdio.h> 2 #include<math.h> 3 #include<string.h> 4 #include<iostream> 5 #include<algorithm> 6 #include<map> 7 #include<set> 8 #include<queue> 9 #include<stack> 10 #define FOR(i,n) for(i=0;i<(n);i++) 11 #define CLR(a) memset(a,0,sizeof(a)) 12 #define CIN(a) scanf("%d",&a) 13 typedef long long ll; 14 using namespace std; 15 pair<int,int> a[200005]; 16 int ans[100005]; 17 int main() 18 { 19 int n,q,i,x; 20 while(scanf("%d",&n)!=EOF) 21 { 22 for(i=0;i<n;i++) scanf("%d%d",&a[i].first,&a[i].second); 23 scanf("%d",&q); 24 for(i=1;i<=q;i++) 25 { 26 scanf("%d",&a[n].first); 27 a[n].second=-i; 28 n++; 29 } 30 sort(a,a+n); 31 int an=-1; 32 for(i=0;i<n;i++) 33 { 34 if(a[i].second<0) ans[-a[i].second]=an; 35 else an=max(an,a[i].second); 36 } 37 for(i=1;i<=q;i++) 38 { 39 printf("%d\n",ans[i]); 40 } 41 } 42 return 0; 43 }
C.优化(3)
详看代码。
1 #include<stdio.h> 2 #include<math.h> 3 #include<string.h> 4 #include<iostream> 5 #include<algorithm> 6 #include<map> 7 #include<set> 8 #include<queue> 9 #include<stack> 10 #define FOR(i,n) for(i=0;i<(n);i++) 11 #define CLR(a) memset(a,0,sizeof(a)) 12 #define CIN(a) scanf("%d",&a) 13 typedef long long ll; 14 using namespace std; 15 ll a[1000005]; //a[i]=x^(i*1e12)=x^((i-1)*1e12)*x^1e12=a[i-1]*a[1] 16 //a[1]=b[1e6] 17 ll b[1000005]; //b[i]=x^(i*1e6) =x^((i-1)*1e6) *x^1e6 =b[i-1]*b[1] 18 //b[1]=c[1e6] 19 ll c[1000005]; //c[i]=x^i=c[i-1]*c[1] 20 //c[1]=x; 21 //x^y=a[y/1e12]*b[y%1e12/1e6]*c[y%1e6]; 22 ll e12=1000000000000LL; 23 ll e6=1000000LL; 24 ll nextrandom(ll y,ll MOD) 25 //随机数生成器,不用改 26 //之所以要随机数生成器,是因为后台没办法上传太大的文件 27 //产生的随机数是为了测试代码效率,所有产生的随机数在[1,10^18]内 28 { 29 y=y*y*MOD; 30 if(y&1LL<<63) y=~y+1; 31 return y%1000000000000000000LL+1; 32 } 33 ll init(ll a[],ll x,ll MOD) 34 { 35 a[0]=1; 36 a[1]=x; 37 for(int i=2;i<=e6;i++) 38 { 39 a[i]=(a[i-1]*x)%MOD; 40 } 41 return a[e6]; 42 } 43 ll f3(ll x,ll y,ll MOD) 44 { 45 return (a[y/e12]*b[y%e12/e6])%MOD*c[y%e6]%MOD; 46 } 47 int main() 48 { 49 ll x,q,y,MOD=1000000007LL,z,ans; 50 while(scanf("%lld",&x)!=EOF)//x<=10^9 51 { 52 init(a,init(b,init(c,x,MOD),MOD),MOD); 53 scanf("%lld%lld",&q,&y);//q<=5000000,y<=10^18 54 ans=0; 55 while(q--) 56 { 57 ans=(ans+f3(x,y,MOD))%MOD; 58 y=nextrandom(y,MOD); 59 } 60 printf("%lld\n",ans); 61 } 62 return 0; 63 }
D.求众数
用map保存每个数出现的次数,用一个set保存出现次数最多的书,并保存当前出现次数最多的次数,每次添加一个数时取出这个数原本出现的次数,将它加一然后和和原本的最多的次数比较,如果相同则将这个数添加进set,如果超过了原本的次数则清空原本的set然后将这个数添加到set内,同时更新最多次数。查询时遍历一遍set输出。
1 #include<stdio.h> 2 #include<math.h> 3 #include<string.h> 4 #include<iostream> 5 #include<algorithm> 6 #include<map> 7 #include<set> 8 #include<queue> 9 #include<stack> 10 #define FOR(i,n) for(i=0;i<(n);i++) 11 #define CLR(a) memset(a,0,sizeof(a)) 12 #define CIN(a) scanf("%d",&a) 13 typedef long long ll; 14 using namespace std; 15 /* 16 操作1:插入一个数 17 操作2:计算众数 18 */ 19 map<int,int> m; 20 set<int> s; 21 int ma; 22 void in(int x){ 23 map<int,int>::iterator it=m.find(x); 24 if(it!=m.end()){ 25 it->second++; 26 if(it->second > ma){ 27 s.clear(); 28 s.insert(x); 29 ma=it->second; 30 }else if(it->second==ma){ 31 s.insert(x); 32 } 33 }else{ 34 m[x]=1; 35 if(ma<=1) s.insert(x); 36 } 37 } 38 void get(){ 39 set<int>::iterator it; 40 for(it=s.begin();it!=s.end();it++){ 41 if(it!=s.begin()) printf(" "); 42 printf("%d",*it); 43 } 44 printf("\n"); 45 } 46 int main(){ 47 int n,x,z; 48 while(scanf("%d",&n)!=EOF){//n<=20w 49 ma=-1; 50 s.clear(); 51 m.clear(); 52 while(n--){ 53 scanf("%d",&z); 54 if(z==1){//插入一个数 55 scanf("%d",&x);//int范围内 56 in(x); 57 }else if(z==2){//求众数 58 get(); 59 } 60 } 61 } 62 return 0; 63 }
E.求中位数
用一个大顶堆和一个小顶堆保存整个数组,维护大顶堆内的所有数小于等于小顶堆内的所有数。如果总数是偶数时,维护两个堆内的元素个数相等,否则大顶堆内数字的个数比小顶堆内多一。查询时,如果大顶堆和小顶堆内的元素个数相同,则分别取出两个堆顶元素相加除以2,否则取出大顶堆的堆顶元素。
1 #include<stdio.h> 2 #include<math.h> 3 #include<string.h> 4 #include<iostream> 5 #include<algorithm> 6 #include<map> 7 #include<set> 8 #include<queue> 9 #include<stack> 10 #define FOR(i,n) for(i=0;i<(n);i++) 11 #define CLR(a) memset(a,0,sizeof(a)) 12 #define CIN(a) scanf("%d",&a) 13 typedef long long ll; 14 using namespace std; 15 priority_queue<int> p1; 16 priority_queue<int,vector<int>,greater<int> > p2; 17 void in(int x){ 18 if(p1.size()==p2.size()){ 19 p2.push(x); 20 p1.push(p2.top()); 21 p2.pop(); 22 }else{ 23 p2.push(x); 24 if(p2.top()<p1.top()) 25 { 26 p1.push(p2.top()); 27 p2.pop(); 28 p2.push(p1.top()); 29 p1.pop(); 30 } 31 } 32 } 33 void mid(){ 34 if(p1.size()==p2.size()){ 35 if(((ll)p1.top()+p2.top())%2==0) printf("%d\n",(int)(((ll)p1.top()+p2.top())/2)); 36 else printf("%.1lf\n",(1.0*p1.top()+p2.top())/2); 37 }else printf("%d\n",p1.top()); 38 } 39 int main(){ 40 int n,z,x; 41 while(scanf("%d",&n)!=EOF){//n<=20w; 42 while(!p1.empty()) p1.pop(); 43 while(!p2.empty()) p2.pop(); 44 while(n--){ 45 scanf("%d",&z); 46 if(z==1){//插入一个数 47 scanf("%d",&x);//int范围内 48 in(x); 49 }else if(z==2){//查询中位数 50 mid(); 51 } 52 } 53 } 54 return 0; 55 }
F.求平均数
两个树状数组维护集合的总和合数量。查询时取出区间值相除。要注意的是总和可能是负数,不要直接拿去求最大公约数。不然可能求出来的公约数是负数。
1 #include<stdio.h> 2 #include<math.h> 3 #include<string.h> 4 #include<iostream> 5 #include<algorithm> 6 #include<map> 7 #include<set> 8 #include<queue> 9 #include<stack> 10 #define FOR(i,n) for(i=0;i<(n);i++) 11 #define CLR(a) memset(a,0,sizeof(a)) 12 #define CIN(a) scanf("%d",&a) 13 typedef long long ll; 14 using namespace std; 15 #define MAXLEN 100005 16 int s[MAXLEN],num[MAXLEN],n; 17 int Lowbit(int x){return x&(-x);} 18 int GCD(int a,int b){return b?GCD(b,a%b):a;} 19 int sum(int c[],int end){ 20 int sum=0; 21 while(end>0){ 22 sum+=c[end]; 23 end-=Lowbit(end); 24 } 25 return sum; 26 } 27 void update(int c[],int pos,int num){ 28 while(pos<=n){ 29 c[pos]+=num; 30 pos+=Lowbit(pos); 31 } 32 } 33 void print(int a,int b){ 34 if(!b) {printf("Error!\n");return;} 35 int gcd=GCD(a,b); 36 printf("%d/%d\n",a/gcd,b/gcd); 37 } 38 int main(){ 39 int z,q,a,b; 40 while(scanf("%d%d",&n,&q)!=EOF){ 41 memset(s,0,sizeof(s)); 42 memset(num,0,sizeof(num)); 43 while(q--){ 44 scanf("%d%d%d",&z,&a,&b); 45 if(z==1){ 46 update(s,a,b); 47 update(num,a,1); 48 }else{ 49 int S=sum(s,b)-sum(s,a-1); 50 int n=sum(num,b)-sum(num,a-1); 51 if(S<0){ 52 printf("-"); 53 print(-S,n); 54 }else print(S,n); 55 } 56 } 57 } 58 return 0; 59 }
G.割绳子问题
二分枚举绳子的长度,每次判断能否得到k个这样的长度的绳子。
1 #include <stdio.h> 2 #include <string.h> 3 #include <math.h> 4 #include <algorithm> 5 using namespace std; 6 int N,K,num[10005]; 7 long long sum; 8 int Judge(int Num) 9 { 10 int Sign=0,i; 11 for(i=0;i<N;i++) 12 { 13 Sign+=(num[i]/Num); 14 } 15 if(Sign>=K) return 1; 16 return 0; 17 } 18 int main() 19 { 20 int i; 21 // freopen("0.in","r",stdin); 22 // freopen("0.out","w",stdout); 23 while(scanf("%d%d",&N,&K)!=EOF) 24 { 25 sum=0; 26 for(i=0;i<N;i++) 27 { 28 scanf("%d",&num[i]); 29 sum+=num[i]; 30 } 31 sum/=K; 32 int L=0,R=sum,Mid; 33 while((R-L)>=1) 34 { 35 if(R-1==L) 36 { 37 if(Judge(R)) {L=R;break;} 38 else if(Judge(L)){L=L;break;} 39 } 40 else 41 { 42 Mid = (L+R)/2; 43 if(Judge(Mid)) L=Mid; 44 else R=Mid; 45 } 46 47 } 48 // printf("%.2f=>%d\n",L,(int)(L+EXP)); 49 printf("%d\n",L); 50 } 51 return 0; 52 }