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
推导过程太精妙了
DP递推的方法:
1 #include <iostream> 2 #include <string.h> 3 #include <stdio.h> 4 #include <vector> 5 #include <map> 6 #define LL long long 7 #define Mod 1000000007 8 using namespace std; 9 //LL d[100005];//局部记忆化 10 //int dcas[100005],cas ; 11 LL Jie[100005]; 12 LL inv[100005]; 13 typedef pair<int,int>PAIR; 14 map<PAIR,int>MAP; 15 vector<int>Pri[100005];//使用变长数组,预处理约数 16 LL pow_mod(LL x,LL n){ 17 LL ans=1; 18 while(n){ 19 if (n & 1) ans =( ans * x ) % Mod; 20 x = (x*x) % Mod; 21 n=n/2; 22 } 23 return ans; 24 } 25 void init(){ 26 // memset(dcas,0,sizeof(dcas)); 27 MAP.clear(); 28 Jie[0]=1; 29 for(int i=1;i<=100000;i++) Jie[i]=(Jie[i-1]*i) % Mod; 30 for(int i=0;i<=100000;i++) inv[i]=pow_mod(Jie[i],Mod-2); 31 32 for(int i=1;i<=100000;i++) Pri[i].clear(); 33 for(int i=2;i<=100000;i++){//类似于筛选法,为每个数填上约数 34 for(int j=i;j<=100000;j+=i){ 35 Pri[j].push_back(i); 36 } 37 } 38 return ; 39 } 40 LL calC(int n,int r){//C=n!/(r! * (n-r)!) 41 return ( Jie[n] * inv[r] % Mod )*inv[n-r] % Mod; 42 } 43 LL dp(int n,int f){ 44 if (n<f) return 0; 45 if (n==f) return 1; 46 PAIR Key=make_pair(n,f); 47 if(MAP.count(Key)) return MAP[Key]; 48 // if (dcas[n]==cas) return d[n]; 49 // dcas[n]=cas;//这也是一种很好的办法,,兼顾空间(省去cas标记)、初始化用时、子结构用时 50 //但是面对极端重复数据就跪了 51 LL ans=calC(n-1,f-1);//得到C(n-1,f-1) //隔板法 52 LL sum=0; 53 for (int i=0;i<(int)Pri[n].size();i++){ 54 int k=Pri[n][i]; 55 sum+=dp(n/k,f); 56 sum = sum % Mod; 57 } 58 ans=(ans-sum + Mod) % Mod; 59 MAP[Key]=ans; 60 return ans; 61 } 62 int main(){ 63 init(); 64 int n,f,t; 65 scanf("%d",&t); 66 while(t--){ 67 scanf("%d%d",&n,&f); 68 if (n<f){ 69 puts("0"); 70 continue; 71 } 72 LL ans=dp(n,f); 73 printf("%I64d\n",ans); 74 } 75 return 0; 76 }