题目noip2011day2
真的很傻逼的错误啊lemon测试用lld输出long long结果Wa成傻子
总体来说题目不难,除了第三题的代码实现略显恶心,其他都是可以完成的题,毕竟是七年前的题了。
先来发一波题解
第一题
很显然的数论题懂一点二项式定理的可以随便坐或者现推杨辉三角也完全没有问题最后再快速幂(好像也不用)
反正怎么都能过的题被我wa了50分真的醉了把n打成k
代码如下
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> using namespace std; const int maxn=1005; const int Mod=10007; long long a,b,n,m,k,ak,bk; long long yh[maxn][maxn]; long long mpow(long long a,long long b) { long long rt=1; while (b) { if (b&1) rt=(rt*a)%Mod; a=(a*a)%Mod; b>>=1; } return rt; } int main() { freopen("factor.in","r",stdin); freopen("factor.out","w",stdout); scanf("%I64d%I64d%I64d%I64d%I64d",&a,&b,&k,&n,&m); for (long long i=1;i<=k;i++) yh[i][1]=1; yh[1][2]=1; for (long long i=2;i<=k;i++) for (long long j=2;j<=k;j++) yh[i][j]=(yh[i-1][j-1]+yh[i-1][j])%Mod; ak=mpow(a,n); bk=mpow(b,m); printf("%I64d\n",ak*bk%Mod*yh[k][1+m]%Mod); return 0; }
第二题看到明显的二分性就想到了二分答案,想法简单但是有几点需要注意。
第一点是可以记录前缀和,这样可以减少查询的时间复杂度,要不就数据结构nlog2似乎只能过80
第二点是要定好二分的边界。比如右边界并不是maxw而是maxw+1,因为mid运算向下取整所以如果去maxw那么maxw这个最优解就找不到了,就wa了一个点
代码如下
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; const int maxn=200005; long long w[maxn],v[maxn],can[maxn],value[maxn],l[maxn],r[maxn],cansum[maxn]; long long s,maxw=-1e9,minlow=1e12,cnt; int n,m; long long work(long long va) { long long rt=0; for (int i=1;i<=m;i++) rt+=(cansum[r[i]]-cansum[l[i]-1])*(value[r[i]]-value[l[i]-1]); return rt; } int main() { freopen("qc.in","r",stdin); freopen("qc.out","w",stdout); scanf("%d%d%I64d",&n,&m,&s); for (int i=1;i<=n;i++) { scanf("%I64d%I64d",&w[i],&v[i]); if (w[i]>maxw) maxw=w[i]; } for (int i=1;i<=m;i++) { scanf("%I64d%I64d",&l[i],&r[i]); } long long lf=0; long long rg=maxw + 1; while (lf<rg) { long long mid=(lf+rg)>>1; cnt=0; memset(can,0,sizeof(can)); memset(cansum,0,sizeof(cansum)); memset(value,0,sizeof(value)); for (int i=1;i<=n;i++) if (w[i]>=mid) can[i]=1; for (int i=1;i<=n;i++) { value[i]=value[i-1]+can[i]*v[i]; cansum[i]=cansum[i-1]+can[i]; } long long tmp=work(mid)-s; if (tmp<0) { if ((-1)*tmp<minlow) minlow=(-1)*tmp; rg=mid; } else { if (tmp<minlow) minlow=tmp; lf=mid+1; } } printf("%I64d",minlow); return 0; }
第三题不说了哈哈考场上瞎搞十分算法
正经一点的话,这道题其实看样子可以用dp之类的东西,想了很久但是没有想清楚,结果可以使用贪心来做。
贪心的正确性比较显然,如果在一来就思路正确的情况下。
贪心思路大概是找到一条能进行加速的路来加速,这条路尽量满足更多人能在下一个地方下车,否则可能会有一个人特别慢在下下个路口卡着从而把前面所有人都卡在那里,即使加了速也没用
什么叫做能加速呢?我们设想一个情景,车先到达,然后车上的所有人等着那个最慢的人。加速之后,车到的更早了,而那个人还是没有来,怎么办呢?还是只有等着。所以这个条件就是到这里的车比最后一个到这里的人来的晚。
好了这样想是不是觉得并不是很难呢?
代码就有点难搞了,主要是思想有点问题想的有点多,一看其他的题解也就那么一点我还有那么多有点吓人,
附上代码
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int maxn=10005; int d[maxn],t[maxn],st[maxn],en[maxn],ar[maxn],sum[maxn],nxt[maxn]; int late[maxn],ans=0; int n,m,k,maxl=1; int mymin(int a,int b) { return a<b?a:b; } int mymax(int a,int b) { return a>b?a:b; } 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-1;i++) scanf("%d",&d[i]); for (int i=1;i<=m;i++) { scanf("%d%d%d",&t[i],&st[i],&en[i]); if (late[st[i]]<t[i]) late[st[i]]=t[i]; sum[en[i]]++; } for (int i=1;i<=n;i++) sum[i]+=sum[i-1]; while (1) { maxl=1; ar[1]=0; for (int i=2;i<=n;i++) ar[i]=mymax(ar[i-1],late[i-1])+d[i-1]; nxt[n]=n; for (int i=n-1;i;i--) { nxt[i]=nxt[i+1]; if (late[i+1]>=ar[i+1]) nxt[i]=i+1; } while (!d[maxl]&&maxl<=n-1) maxl++; if (maxl==n||k==0) break; for (int i=maxl+1;i<=n-1;i++) if (d[i]&&sum[nxt[maxl]]-sum[maxl]<sum[nxt[i]]-sum[i]) maxl=i; if (sum[nxt[maxl]]-sum[maxl]==0) break; int dd=1e9; for (int i=maxl+1;i<=nxt[maxl]-1;i++) { dd=mymin(dd,ar[i]-late[i]); } dd=mymin(dd,d[maxl]); dd=mymin(k,dd); k-=dd; d[maxl]-=dd; } for (int i=1;i<=m;i++) ans+=(ar[en[i]]-t[i]); printf("%d\n",ans); return 0; }
好了考试总结完了刷题去。