您的位置:首页 > 其它

NOIP2011解题报告-Day2

2012-08-13 21:31 453 查看
计算系数

二项式定理。杨辉三角堆组数+快速幂。Noip的时候还没学二项式定理。。。

const p=10007;maxk=1000;
var  a,b,k,n,m:longint;
function C(k,m:longint):longint;
var f:array[0..maxk,0..maxk] of longint;
i,j,n:longint;
begin
n:=k-m;
if(n=0)or(m=0)then exit(1);
for i:=0 to n do f[i][0]:=1;
for j:=0 to m do f[0][j]:=1;
for i:=1 to n do
for j:=1 to m do
f[i][j]:=(f[i-1][j]+f[i][j-1])mod p;
exit(f
[m]);
end;
function exp(a,b:longint):longint;
var ans,temp:int64;
begin
ans:=1;temp:=a;
while b<>0 do
begin
if((b and 1)=1)then ans:=(ans*temp)mod p;
temp:=(temp*temp)mod p;
b:=b shr 1;
end;
exit(ans);
end;
begin
readln(a,b,k,n,m);
if(n=0)and(m=0)
then begin
writeln(0);
halt;
end;
a:=a mod p;b:=b mod p;
writeln(C(k,m)*int64(exp(a,n))*exp(b,m) mod p);
end.


聪明的质检员

考场上没看懂题目。。。

发现Y是关于w的减函数,于是二分W,维护前缀和在O(n)的复杂度求出Y。总复杂度O(N log(max{wi}))

PS:w离散化后二分复杂度应该是O(N log(N))时间没有快多少,可能出现在对数上影响不大.

program qc;
uses math;
var n,m,i,low,high,mid,tot:longint;
s,y:int64;
l,r,w,v,sta:array[0..200000] of longint;
count,sigmaV:array[0..200000] of int64;
f:array[0..1000000] of boolean;
function calc(mid:longint):int64;
var i:longint;
begin
calc:=0;count[0]:=0;sigmaV[0]:=0;
for i:=1 to n do
begin
count[i]:=count[i-1];
sigmaV[i]:=sigmaV[i-1];
if w[i]>mid
then begin
inc(count[i]);
inc(sigmaV[i],v[i]);
end;
end;
for i:=1 to m do
inc(calc,(count[r[i]]-count[l[i]-1])*
(sigmaV[r[i]]-sigmaV[l[i]-1]));
end;
begin
high:=0;low:=maxlongint;
readln(n,m,s);
for i:=1 to n do
begin
readln(w[i],v[i]);
low:=min(w[i],low);
high:=max(w[i],high);
f[w[i]]:=true;
end;
for i:=1 to m do
readln(l[i],r[i]);
//===lisuanhua===
tot:=1;sta[tot]:=low-1;
for i:=low to high do
if f[i] then begin
inc(tot);
sta[tot]:=i;
end;
inc(tot);sta[tot]:=high+1;
//===============
low:=1;high:=tot;
while low+1<high do
begin
mid:=(low+high)>>1;
y:=calc(sta[mid]);
if y>s then low:=mid
else high:=mid;
end;
writeln(min(abs(s-calc(sta[low])),abs(s-calc(sta[high]))));
end.
//================================================
program qc;
uses math;
var n,m,i,low,high,mid:longint;
s,y:int64;
l,r,w,v:array[0..200000] of longint;
count,sigmaV:array[0..200000] of int64;
function calc(mid:longint):int64;
var i:longint;
begin
calc:=0;count[0]:=0;sigmaV[0]:=0;
for i:=1 to n do
begin
count[i]:=count[i-1];
sigmaV[i]:=sigmaV[i-1];
if w[i]>mid
then begin
inc(count[i]);
inc(sigmaV[i],v[i]);
end;
end;
for i:=1 to m do
inc(calc,(count[r[i]]-count[l[i]-1])*
(sigmaV[r[i]]-sigmaV[l[i]-1]));
end;
begin
high:=0;low:=maxlongint;
readln(n,m,s);
for i:=1 to n do
begin
readln(w[i],v[i]);
low:=min(w[i],low);
high:=max(w[i],high);
end;
for i:=1 to m do
readln(l[i],r[i]);
dec(low);inc(high);
while low+1<high do
begin
mid:=(low+high)>>1;
y:=calc(mid);
if y>s then low:=mid
else high:=mid;
end;
writeln(min(abs(s-calc(low)),abs(s-calc(high))));
end.


观光公交

首先考虑k=0;如何计算出答案

Leave[i]为最早允许离开i的时间 leave[i]=max{t[j]}(a[j]=i)

可以再读入数据时处理出来leave[a[i]]:=max(T[i],leave[a[i]]);

Get[i]为到达i站时间 Get[i]=max(get[i-1],leave[i-1])+D[i-1];

枚举计算每个旅客的旅行时间

Ans=sigma(i=1..m) (get[b[i]]-T[i])

K=1时枚举给哪个D[i]减一即可

满分做法:贪心

发现给某个D[i]减一后会使后面连续的人等车的车站的乘客提前上车。直到一个车站leave[j]>get[j](车等人),人来的时间是不能跟改变的。所以提前了上车时间的旅客提前了的一分钟要在等人上浪费掉,所以旅行时间不会减小。也就是说令right[i]为i后面第一个满足车等人的站点。

那么在i+1到right[i]这个区间内下车的人的旅行时间都会减小一。维护下车人数的前缀和S。

给D[i]减一所减小的答案为S[right[i]]-S[right[i]]。每次找到最大的加速。找最大可以O(n)暴力,也可以维护堆O(log n)。减的时候有3种情况

1.k很小全部用掉

2.D[i]很小全部减掉(必须保证每个D[i]>=0)

3.i+1..tright[i]中最小的Get[j]-Leave[j](把这个值减成负的会破坏现有的区间的关系,减成负的就变成车等人了)

从这3个值中取最小值用掉

然后O(n)重新计算i..right[i]-1的right值

没写堆,在RQ上过了。Noip的数据很厚道。

program bus;
uses math;
CONST
maxn=1000;
maxm=10000;
maxk=1000000;
var a,b,t:array[0..maxm] of longint;
right,tot,s,cnt,GetIn,GetOff,get,leave,d:array[0..maxn+1] of longint;
p,count,n,m,k,i,j,maxcnt,maxi,temp:longint;
ans:int64;
BEGIN
read(n,m,k);
for i:=1 to n-1 do read(D[i]);
for i:=1 to m do
begin
read(t[i],a[i],b[i]);
inc(GetOff[b[i]]);inc(GetIn[a[i]]);
leave[a[i]]:=max(leave[a[i]],t[i]);
end;
for i:=2 to n do
get[i]:=max(leave[i-1],get[i-1])+D[i-1];
p:=1;
for i:=1 to n do
begin
while((p<n)and(leave[p]<get[p]))or(p<=i)do inc(p);
right[i]:=p;
end;
for i:=1 to n do
s[i]:=s[i-1]+GetOff[i];
while k>0 do
begin
maxcnt:=0;
for i:=1 to n-1 do
if (s[right[i]]-s[i]>maxcnt)and(D[i]>0)
then begin
maxcnt:=s[right[i]]-s[i];
maxi:=i;
end;
if maxcnt=0
then break
else begin
temp:=maxlongint;j:=maxi+1;
while(j<n)and(leave[j]<get[j])do
begin
temp:=min(get[j]-leave[j],temp);
inc(j);
end;
temp:=min(D[maxi],temp);temp:=min(k,temp);
dec(k,temp);dec(D[maxi],temp);
for j:=maxi+1 to right[i] do
get[j]:=max(get[j-1],leave[j-1])+D[j-1];
p:=maxi;count:=GetOff[maxi];
for j:=maxi to right[i]-1 do
begin
while((p<n)and(leave[p]<get[p]))or(p<=j)do inc(p);
if p>=right[j] then break;
right[j]:=p;
end;
end;
end;
get[1]:=leave[1];get
:=max(get
,leave
);
for i:=1 to m do
inc(ans,get[b[i]]-t[i]);
writeln(ans);
END.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: