NOIP 2011 提高组 Day2 校模拟 7.11
数论只会GCD 模拟只会猜题意 贪心只能过样例!!!!!
上午身体不适,基本上是强行趴在那写题。也不知道思路在哪个状态,看到T1第一想法居然连暴力都不是了。第一想法,居然是打表2333
T1: 计算系数
为什么我脑子里第一反应是打表…
是的这是我用画图打的草稿(还有一部分打到草稿纸上了)
然后我就写出来以下的天秀打表
// if(a==b&&b==1){ // if(k<=10){ // switch(k){ // case 0:{ // cout<<0; // break; // } // case 1:{ // cout<<0; // break; // } // case 2:{ // if(n==0||m==0){ // //2+0 // cout<<1; // } // else if(n==1){ // //1+1 // cout<<2; // } // else{ // cout<<0; // } // break; // } // case 3:{ // if(n==0||m==0){ // cout<<1;// 3+0 // } // else if(n==2||m==2){ // //2+1 // cout<<3; // } // else{ // cout<<0; // } // break; // } // case 4:{ // if(n==3||m==3){ // //3+1 // cout<<4; // } // else if(n==2){ // cout<<6; // //2+2 // } // else if(n==0||m==0){ // cout<<1;//4+0 // } // else{ // cout<<0; // } // break; // } // case 5:{ // if(n==4||m==4){//4+1 // cout<<5; // } // else if(m==2||n==2){ // //3+2 // cout<<10; // } // else if(m==0||n==0){ // //5+0 // cout<<1; // } // else{ // cout<<0; // } // break; // } // case 6:{ // if(n==5||m==5){ // cout<<6;//5+1 // } // else if(m==0||n==0){ // //6+0 // cout<<1; // } // else if(m==2||n==2){//4+2 // cout<<15; // } // else if(m==3){//3+3 // cout<<20; // } // else{ // cout<<0; // } // break; // } // case 7:{ // if(n==6||m==6){ // cout<<7;//6+1 // } // else if(n==0||m==0){ // cout<<1; // //7+0 // } // else if(m==2||n==2){//5+2 // cout<<21; // } // else if(m==3||n==3){//4+3 // cout<<35; // } // else{ // cout<<0; // } // break; // } // case 8:{ // // break; // } // case 9:{ // // break; // } // case 10:{ // // break; // } // } // } // else{ // // } // }
打到8的时候,终于打通了我的脑回路。打表找规律…这行为是真滴流氓。
果然跟这个题目差不多,factor,类似于因子嘛(别挖我的十五级英语!!!),然后就发觉这东西是和前一项有联系的(而且还是相加的关系!)就是说,你的这个因子总是与上一个数的因子相关联。我就…痴痴地写出了以下正解…
#include<iostream> #include<algorithm> using namespace std; long long a,b,k,n,m; long long A[2000][2000]; long long ans; const int Mogic=10007; int power(int a,int b){ int ans=1; for(;b;b>>=1){ if(b&1){ ans=(long long)ans*a%Mogic; } a=(long long)a*a%Mogic; } //cout<<"返回值为"<<ans<<endl; return ans; } int main(){ freopen("factor.in","r",stdin); freopen("factor.out","w",stdout); cin>>a>>b>>k>>n>>m; A[2][2]=1;A[2][0]=1; A[2][1]=2; for(int j=3;j<=k;j++){ A[j][0]=1; A[j][j]=1; A[j][j-1]=j; A[j][1]=j; } //对于每个数来说 //他的 i分子与 k-i是相同的 for(int x=4;x<=k;x++){ for(int i=2;i<=k/2;i++){ A[x][i]=A[x-1][i]+A[x-1][x-i]; A[x][i]%=Mogic; A[x][x-i]=A[x][i]; } } if(a==b&&b==1){ cout<<A[k][n]%Mogic; } else{ //cout<<"调用二号"<<endl; A[k][n]%=Mogic; a%=Mogic; b%=Mogic; long long dele; dele=power(a,n)*power(b,m); dele%=Mogic; //cout<<"dele为 "<<dele<<endl; //cout<<"A k n "<<A[k][n]<<endl; ans=A[k][n]*dele; ans%=Mogic; cout<<ans; } }
记着开LongLong!
T2: 聪明的质监员
这个质检员的形容词是聪明?
一股腐败气息(哼)
当时拿到之后脑子还是懵兮兮的,直接跟着题走去模拟了。
#include<iostream> #include<cmath> #include<cstdio> using namespace std; const int MAXN=999999; int n,m,s; int w[MAXN]; int V[MAXN]; struct Factory{ int a; int b; }Zone[MAXN]; int ans; int res; bool Good[MAXN]; int main(){ //freopen("qc.in","r",stdin); //freopen("qc.out","w",stdout); res=9999999; int maxx=0; int maxx_forzone=0; int minxx_forzone=900; cin>>n>>m>>s; for(int i=1;i<=n;i++){ cin>>w[i]>>V[i]; maxx=max(maxx,w[i]); } int x,y; for(int i=1;i<=m;i++){ cin>>x>>y; Zone[i].a=x; Zone[i].b=y; maxx_forzone=max(maxx_forzone,y); minxx_forzone=min(minxx_forzone,x); } for(int W=1;W<=maxx;W++){ //这题估计用二分 //二分这个W,找到一个会产生正数差和负数差的平衡点 //只要这个W一大,那么相应的ans值就会减小。也就是说, //当二分出差值一正一副的时候,对比两边的差值,得出最终答案。 ans=0; for(int i=minxx_forzone;i<=maxx_forzone;i++){ if(w[i]>=W){ Good[i]=true; } else{ Good[i]=false; } } for(int i=1;i<=m;i++){ int tot=0; int val=0; for(int j=Zone[i].a;j<=Zone[i].b;j++){ if(Good[i]){ tot++; val+=V[j]; } } ans+=tot*val; // if(s-ans<0&&abs(s-ans)>res){ // break; // } int index=abs(s-ans); res=min(res,index); } //cout<<"选择了 "<<W<<" 所产生的区间和为 "<<ans<<endl; } cout<<res; }
最有趣的是写着写着忽然就醒悟“哦!这是道二分题!!”
然后看了一眼表“我去我第一题是模拟了多久…还剩十分钟…算了把这题打完拉倒了,不管二分的事了”
喜爆零~!(好像是有个题目意思理解错了吗?)
正解其实也就是二分+前缀和了。没办法T1玩的太嗨,T2有思路了也没用。
#include<iostream> #include<cstring> #include<cmath> #include <stdlib.h> using namespace std; const int MAXN=2000010; int n,m; long long ans; long long S; long long MAX_forR; long long MIN_forL; long long w[MAXN]; long long v[MAXN]; struct section{ int l; int r; }Zone[MAXN]; long long Y,sum; long long FrontSum_n[MAXN];//你瞧,英语十级的气息 long long FrontSum_v[MAXN]; bool check(int W){ Y=0;sum=0; memset(FrontSum_n,0,sizeof(FrontSum_n)); memset(FrontSum_v,0,sizeof(FrontSum_v)); for(int i=1;i<=n;i++){ if(w[i]>=W){ FrontSum_n[i]=FrontSum_n[i-1]+1; FrontSum_v[i]=FrontSum_v[i-1]+v[i]; } else{ FrontSum_n[i]=FrontSum_n[i-1]; FrontSum_v[i]=FrontSum_v[i-1]; } } for(int i=1;i<=m;i++){ Y+=(FrontSum_n[Zone[i].r]-FrontSum_n[Zone[i].l-1])*(FrontSum_v[Zone[i].r]-FrontSum_v[Zone[i].l-1]); } sum=llabs(Y-S); if(Y>S){ return true; } else{ return false; } } int main(){ cin>>n>>m>>S; MIN_forL=999999999; ans=999999999999999; for(int i=1;i<=n;i++){ cin>>w[i]>>v[i]; MAX_forR=max(MAX_forR,w[i]); MIN_forL=min(MIN_forL,w[i]); } for(int i=1;i<=m;i++){ cin>>Zone[i].l; cin>>Zone[i].r; } int left=MIN_forL-1; int right=MAX_forR+2; int mid; while(left<=right){ mid=(left+right)/2; if(check(mid)){ left=mid+1; } else{ right=mid-1; } if(sum<ans){ ans=sum; } } cout<<ans; }
(话说long long设定个最大值是真的麻烦…那个9写少了居然能炸一半的数据…)
T3: 观光公交
考场的时候没看,毕竟时间没了。
下来之后看了看题,感觉上来说…有点像以前做过的一个缆车的题…但又不一样。
这里有一个逻辑上的点,就是如果你开了氮气加速但到站了仍然需要等待最后一个来的人,那这波氮气就开的挺亏的。
公交车氮气加速,你这怕是速度与激情,牛逼!
代码来自LuoGu题解,看懂了但是真的不想再重新写一遍了…
/*last[ i ] : 最后一个人到达i站点的时间。 sum[ i ] : 到i站点的总人数。 enter[ i ] : 公交车到i站点的最少时间。 g[ i ] : 每个站点所能影响到的最远站点,即要求的影响。*/ #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> //完全get,但精神受到了昨天175行重构代码结果有Bug没法Debug的重创 //今天!决定!不!重!构!啦! #define N 30001 using namespace std; int n,m,k; int dis[N],last[N],g[N],enter[N]; int ans,sum[N],maxx = -1; struct node{ int time,start,end; }a[N]; inline void bus(int x){ while(x){ --x; g[n] = g[n - 1] = n; int tar; maxx = -1; for(int i = n - 2;i >= 1;-- i){ if(enter[i + 1] <= last[i + 1])//下一个点如果等待 g[i] = i + 1;//最多影响到下一个 else //不等待 g[i] = g[i + 1];//继续减少后面的时间 } for(int i = 1;i < n;++ i){//for边数 int tmp = sum[g[i]] - sum[i];//最多影响的人数 if(tmp > maxx && dis[i] > 0){ maxx = tmp; tar = i;//标记最优情况减的点 } } ans -= maxx;//更新ans dis[tar] --;//减掉dis for(int i = 2;i <= n;++ i){ enter[i] = max(enter[i - 1],last[i - 1]) + dis[i - 1];//重新更新enter } } return; } int main(){ scanf("%d%d%d",&n,&m,&k);// for(int i = 1;i < n;++ i) scanf("%d",&dis[i]);// for(int i = 1;i <= m;++ i) scanf("%d%d%d",&a[i].time,&a[i].start,&a[i].end); for(int i = 1;i <= m;++ i){ last[a[i].start] = max(last[a[i].start],a[i].time);//最后一个人到a[i].start站点的时间 和到这个点的时间取max sum[a[i].end] ++; } enter[1] = last[1]; for(int i = 1;i <= n;++ i) sum[i] += sum[i - 1];//到i站点的总人数 前缀和处理 for(int i = 2;i <= n;++ i) enter[i] = max(enter[i - 1],last[i - 1]) + dis[i - 1];//公车到i站点的最少时间 和最后到的时间取max for(int i = 1;i <= m;++ i) ans += enter[a[i].end] - a[i].time;//处理出不加加速器的answer,后面就可以直接减啦~ bus(k); printf("%d",ans); return 0; }//