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(完结撒花)。