首页 > 代码库 > *Codeforces Round #251 (Div. 2)AK)
*Codeforces Round #251 (Div. 2)AK)
A、确定性数学计算,水,读题要快
1 #include<iostream> 2 #include<stdio.h> 3 4 using namespace std; 5 int N,d; 6 int main(){ 7 while(~scanf("%d%d",&N,&d)){ 8 int cnt=0; 9 for(int i=0;i<N;i++){ 10 int t; 11 scanf("%d",&t); 12 cnt+=t; 13 } 14 if (cnt+(N-1)*10>d){ 15 printf("-1\n");continue; 16 } 17 int ans=(N-1)*10/5+(d-(cnt+(N-1)*10))/5; 18 cout<<ans<<endl; 19 20 } 21 return 0; 22 }
B、贪心,章节少的课先学,排序+贪心+long long 处理细节
1 #include<iostream> 2 #include<stdio.h> 3 #include<algorithm> 4 #include<stdlib.h> 5 6 using namespace std; 7 int n,s; 8 int a[100005]; 9 10 int main(){ 11 while(~scanf("%d%d",&n,&s)){ 12 for(int i=0;i<n;i++) scanf("%d",&a[i]); 13 sort(a,a+n); 14 long long ans=0; 15 s++; 16 for(int i=0;i<n;i++){ 17 if (s>1) s--; 18 ans+=(long long)s*a[i];//在s前面加了long long 就过了,伤不起,应该是要强制转化成long long,不然后面两个int计算只保留了int部分 19 } 20 cout<<ans<<endl; 21 } 22 return 0; 23 }
注意:ans+=(long long)s*a[i];//在s前面加了long long 就过了,伤不起,应该是要强制转化成long long,不然后面两个int计算只保留了int部分
C、确定性数学证明(奇偶证明)+策略模拟 (很容易粗)
我的写法还是不是很好,下标算来算去,这道题一定要考虑到p-k==0的情况,不然会出错,
给自己出数据的时候,没有考虑到这个情况,比赛的时候可能就会跪了
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 using namespace std; 5 int n,k,p,d;//p个偶数组合 6 int a[100006],b[100006]; 7 int main() 8 { 9 while(~scanf("%d%d%d",&n,&k,&p)){ 10 int cnt1=0,cnt2=0; 11 for(int i=0;i<n;i++){ 12 int num; 13 scanf("%d",&num); 14 if (num % 2 == 0) a[cnt1++]=num;else b[cnt2++]=num; 15 } 16 int p1=p,p2=k-p; 17 if (cnt1+cnt2/2<p1){ 18 cout<<"NO"<<endl; 19 continue; 20 } 21 if (cnt1>=p1){ 22 if (cnt2<p2 || cnt2 % 2 != p2 % 2){ 23 cout<<"NO"<<endl; 24 continue; 25 } 26 cout<<"YES"<<endl; 27 if (p2>0){ 28 for(int i=0;i<p1;i++){ 29 cout<<1<<" "<<a[i]<<endl; 30 } 31 cnt1=cnt1-p1; 32 cout<<cnt1+(cnt2-p2)+1<<" "; 33 for(int i=p1;i<p1+cnt1;i++){ 34 cout<<a[i]<<" "; 35 } 36 for(int i=0;i<(cnt2-p2)+1;i++){ 37 if (i==cnt2-p2){ 38 cout<<b[i]<<endl; 39 }else cout<<b[i]<<" "; 40 } 41 for(int i=cnt2-p2+1;i<cnt2;i++){ 42 cout<<1<<" "<<b[i]<<endl; 43 } 44 }else{ 45 cout<<cnt1-p1+1+cnt2; 46 for(int i=0;i<cnt1-p1+1;i++) cout<<" "<<a[i]; 47 for(int i=0;i<cnt2;i++) cout<<" "<<b[i]; 48 cout<<endl; 49 for(int i=cnt1-p1+1;i<cnt1;i++){ 50 cout<<1<<" "<<a[i]<<endl; 51 } 52 } 53 54 continue; 55 }else { 56 if (cnt2-(p1-cnt1)*2<p2 || (cnt2-(p1-cnt1)*2) % 2 != p2 %2 ){ 57 cout<<"NO"<<endl; 58 continue; 59 } 60 cout<<"YES"<<endl; 61 for(int i=0;i<cnt1;i++){ 62 cout<<1<<" "<<a[i]<<endl; 63 } 64 65 d=0; 66 if (p2>0){//这里是个大坑 67 for(int i=cnt1;i<p1;i++){ 68 cout<<2<<" "<<b[d++]<<" "<<b[d++]<<endl; 69 } 70 }else {//p2==0 71 for(int i=cnt1;i<p1-1;i++){ 72 cout<<2<<" "<<b[d++]<<" "<<b[d++]<<endl; 73 } 74 cout<<cnt2-d<<" "; 75 for(int i=d;i<cnt2;i++){ 76 if (i==cnt2-1) cout<<b[i]<<endl;else cout<<b[i]<<" "; 77 d++; 78 } 79 } 80 if (cnt2==d) continue; 81 if (cnt2 - d > p2){ 82 83 cout<<cnt2-d-p2+1<<" "; 84 for(int i=d;i<cnt2-p2+1;i++){ 85 if (i==cnt2-p2){ 86 cout<<b[i]<<endl; 87 }else { 88 cout<<b[i]<<" "; 89 } 90 } 91 for(int i=cnt2-p2+1;i<cnt2;i++) cout<<1<<" "<<b[i]<<endl; 92 }else {//cnt2-d==p2 93 for(int i=d;i<cnt2;i++) cout<<1<<" "<<b[i]<<endl; 94 } 95 } 96 } 97 return 0; 98 99 }
D、三分搜索+贪心+枚举(也可以排序+二分搜索(优化1/2的常数))
题目描述:给定A数组,n个数,给定B数组,m个数,
定义操作:一次任选数组,任选一个元素,可以增大或减少;
求一个最小操作数,使得改变后,A中最小的元素a大于等于B中最大的元素b。
方法就是上面所说的:
1、三分F(X):假设最终a=b=x,注意a和b肯定相等,因为我们是向中间取数的。不等没有相等步数少。
这样能确定x的范围,min(A)到max(B),因为我们肯定是增大A中元素,减小B中元素。
但是我不能证明是凹性的函数啊!!!
2、选定x,贪心的策略就定下来了:
if (a[i]<x) ans+=x-a[i];//尽可能用最小的步数达成目的
3、三分写法再总结:
(1)用t控制次数,不写while
(2)最终在[l,r]的范围内找到极值
(3)注意画图,尽可能地包含极值点
下面是别人的证明:
1 #include<iostream> 2 #include<stdio.h> 3 #include<algorithm> 4 #include<stdlib.h> 5 #define LL long long 6 using namespace std; 7 int n,m; 8 LL a[100005],b[100005]; 9 LL F(LL x) 10 { 11 //将A数组中的变成至少为x;(增加) 12 LL ans=0; 13 for(int i=0; i<n; i++) //可二分优化 14 { 15 if (a[i]<x) ans+=x-a[i]; 16 } 17 //将A数组中的变成最多为x;(减少) 18 for(int i=0; i<m; i++) 19 { 20 if (b[i]>x) ans+=b[i]-x; 21 } 22 return ans; 23 } 24 int main() 25 { 26 // freopen("out.txt","w",stdout); 27 while(~scanf("%d%d",&n,&m)) 28 { 29 LL Min=1e12,Max=-1; 30 for(int i=0; i<n; i++) 31 { 32 scanf("%I64d",&a[i]); 33 Min=min(Min,a[i]); 34 } 35 for(int i=0; i<m; i++) 36 { 37 scanf("%I64d",&b[i]); 38 Max=max(Max,b[i]); 39 } 40 sort(a,a+n); 41 sort(b,b+m); 42 LL l=Min-1,r=Max+1; 43 for(int i=0; i<100; i++) 44 { 45 int m1=l+(r-l)/3,m2=r-(r-l)/3; 46 if (F(m1)<F(m2)) r=m2; 47 else l=m1; 48 } 49 LL ans=F(l); 50 for(LL i=l;i<=r;i++) ans=min(ans,F(i)); 51 cout<<ans<<endl; 52 } 53 return 0; 54 }
E、dp(关键是记忆化的思想)+乘法逆(数学考得好啊)
ps:题解:http://codeforces.com/blog/praveen123
推导过程太精妙了
暂时超时的代码:
1 #include <iostream> 2 #include <string.h> 3 #include <stdio.h> 4 #define LL long long 5 #define Mod 1000000007 6 LL d[100005]; 7 LL Jie[100005]; 8 using namespace std; 9 void gcd(LL a,LL b,LL& d,LL& x,LL& y){ 10 if (!b){d=a;x=1;y=0;} 11 else {gcd(b,a%b,d,y,x);y-=x*(a/b);} 12 } 13 LL inv(LL a,LL n){ 14 LL d,x,y; 15 gcd(a,n,d,x,y); 16 return d==1?(x+n)%n:-1; 17 } 18 void init(){//生成C[x,f]的数表,n是最大范围 19 Jie[0]=1; 20 for(int i=1;i<=100005;i++) 21 Jie[i]=(Jie[i-1]*i)%Mod; 22 } 23 LL calC(int n,int r){ 24 LL ans=Jie[n]; 25 LL k=Jie[r] * Jie[n-r];k=k%Mod; 26 return (ans * inv(k,Mod)) % Mod; 27 } 28 LL dp(int n,int f){ 29 // cout<<"n="<<n<<","<<"f="<<f<<endl; 30 if (n<f) return 0; 31 if (n==f) return 1; 32 if (d[n]!=-1) return d[n]; 33 LL ans=calC(n-1,f-1);//得到C(n-1,f-1) //隔板法 34 LL sum=0; 35 for(int i=2;i<=n;i++){ 36 if (n % i == 0) sum+=dp(n/i,f); 37 sum=sum % Mod; 38 } 39 ans=(ans-sum + Mod) % Mod; 40 return d[n]=ans; 41 } 42 int main(){ 43 init(); 44 int n,f,t; 45 scanf("%d",&t); 46 while(~scanf("%d%d",&n,&f)){ 47 if (n<f){ 48 cout<<0<<endl; 49 continue; 50 } 51 memset(d,-1,sizeof(d)); 52 LL ans=dp(n,f); 53 cout<<ans<<endl; 54 } 55 return 0; 56 }