TEST2018.4.14(Noip2011提高组Day2)
为2019级lizw0520zzh的学生献上福利!!!
题目描述(感谢洛谷爸爸): 计算系数 聪明的质检员 观光公交
T1 计算系数:
小学奥数!!!
学过杨辉三角的同学应该很容易推出来:
存在答案数组使得 ans[i][j]=(ans[i-1][j]*a+ans[i-1][j-1]*b);
其中ans[i][j]表示i次方的第j项的系数,故而答案就是ans[k][m+1]。
接下来就是代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<queue> #define dnt long long using namespace std; const dnt MOD=10007; const int N=1000+23; dnt ans[N][N]; dnt k,m,n,a,b; void init() { ans[0][1]=1; ans[1][1]=a;ans[1][2]=b; for(int i=2;i<=k;i++) for(int j=1;j<=i+1;j++) ans[i][j]=(ans[i-1][j]*a%MOD+ans[i-1][j-1]*b%MOD)%MOD; } int main() { freopen("factor.in","r",stdin); freopen("factor.out","w",stdout); cin>>a>>b>>k>>n>>m; init(); cout<<ans[k][m+1]<<endl; return 0; }
不开long long见祖宗!
T2 聪明的质检员:
题外话:事后看了一下洛谷的题解,发现大家都在吐槽这个质检员会不会被开除的问题QAQ(我也这么觉得)。
因为对于不同的W,存在不同的S,所以很显然可以二分答案。
即二分W的值,求出每次Y的值,直到找到两个W值 left和right 使得 Y 最接近 S 。
注意这里一定要检查 left和right 所得到 Y 值与 S 的差的绝对值谁更小(被坑了T_T)。
代码:注意check()可以有多种写法,以下为其中之一
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<iostream> #define maxn 210000 using namespace std; long long s1[maxn],s2[maxn]; int l[maxn],r[maxn]; int w[maxn],v[maxn]; int n,m; long long S; long long check(int x) { memset(s1,0,sizeof(s1)); memset(s2,0,sizeof(s2)); for (int i=1;i<=n;i++) { s1[i]=s1[i-1];s2[i]=s2[i-1]; if (w[i]>=x) { s1[i]+=1; s2[i]+=v[i]; } } long long res=0; for(int i=0;i<m;i++) res+=(s1[r[i]]-s1[l[i]-1])*(s2[r[i]]-s2[l[i]-1]); return res; } int main(){ freopen("qc.in","r",stdin); freopen("qc.out","w",stdout); cin>>n>>m>>S; for(int i=1;i<=n;i++) scanf("%d%d",&w[i],&v[i]); for(int i=0;i<m;i++) scanf("%d%d",&l[i],&r[i]); int left=0,right=*max_element(w+1,w+n+1); while(left+1<right){ int mid=(left+right)/2; if (check(mid)<=S)right=mid; else left=mid; } cout<<min(abs(check(left)-S),abs(S-check(right))); return 0; }
T3 观光公交:
吐槽一波:大家看着很复杂,其实巨简单,就是贪心。但是看你贪心策略是什么了。
一开始我想的是找出每段路有几个人走过,先算出总时间,然后对走的人数最多的路进行加速(注意每段路时间不能小于零)。
然后就WA了(想爆粗)。
于是考完试后照例经典算法:百度搜索了一波,发现是贪心策略有错。
正确思路大家可以参照以下网址,在此不加以赘述:
本人使用第二种方法依葫芦画瓢写了一下,结果在累加距离时加错了,改了好久【我们的同学,一定要细心啊(强行学老李口气)】。
代码【史上最简单写法(有点小夸张)】:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1000+233; int n,m,k,ans=0; int arrive[N],leave[N],off[N],d[N],sum[N]; int main() { //freopen("bus.in","r",stdin); //freopen("bus.out","w",stdout); scanf("%d%d%d",&n,&m,&k); for(int i=1;i<n;i++) scanf("%d",d+i); for(int i=1,a,b,t;i<=m;i++) scanf("%d%d%d",&t,&a,&b),off[b]++,leave[a]=max(leave[a],t),ans-=t; int now,use; while(k) { for(int i=1;i<=n;i++) arrive[i]=max(arrive[i-1],leave[i-1])+d[i-1]; now=0; for(int i=n;i>1;i--) if(!d[i-1]) sum[i-1]=0; else {sum[i-1]=off[i];if(arrive[i]>leave[i]) sum[i-1]+=sum[i];} for(int i=1;i<n;i++) if(now<sum[i]) now=sum[i],use=i; if(!now) break; d[use]--;k--; } for(int i=1;i<=n;i++) arrive[i]=max(arrive[i-1],leave[i-1])+d[i-1]; for(int i=1;i<=n;i++) ans+=arrive[i]*off[i]; printf("%d",ans); return 0; }
那么题解到此结束了!祝大家早日AK(完结撒花)。