NOIP2017提高组 模拟赛16(总结)
第一题 比赛 (原题 noip2011 瑞士轮)
【题目描述】
【解题思路】
每一次比赛过后,赢的N名选手是有序的,输的N名选手也是有序的(赢的+1,赢的那N个都+1,对位置不会有影响。输的也是一样)。直接用O(N)的时间归并即可。
时间复杂度:O(N logN+RN)
【代码】
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=200100;
int n,t,ned;
int D[N],q1[N],q2[N],p1,p2;
struct data { int g,p,id; } d[N];
bool cmp(int A,int B) { return (d[A].g>d[B].g || (d[A].g==d[B].g && A<B)); }
int main()
{
freopen("2241.in","r",stdin);
freopen("2241.out","w",stdout);
scanf("%d%d%d",&n,&t,&ned);
n<<=1;
for(int i=1;i<=n;i++) scanf("%d",&d[i].g);
for(int i=1;i<=n;i++) scanf("%d",&d[i].p),D[i]=i;
sort(D+1,D+1+n,cmp);
while(t--)
{
p1=0; p2=0;
for(int i=1;i<=n;i+=2)
if(d[D[i]].p>d[D[i+1]].p)
{
d[D[i]].g++;
q1[++p1]=D[i]; q2[++p2]=D[i+1];
} else
{
d[D[i+1]].g++;
q1[++p1]=D[i+1]; q2[++p2]=D[i];
}
p1=p2=1;
for(int i=1;i<=n;i++)
{
if(p1<=(n>>1))
{
if(d[q1[p1]].g>d[q2[p2]].g || (d[q1[p1]].g==d[q2[p2]].g && q1[p1]<q2[p2]))
D[i]=q1[p1++]; else D[i]=q2[p2++];
} else D[i]=q2[p2++];
}
}
printf("%d\n",D[ned]);
return 0;
}
第二题 奶牛跑步
【题目描述】
【解题思路】
比赛持续了T=C*L/speed_max
奶牛i超过奶牛j的次数Pij=T*(speed_i-speed_j)/C=L/speed_max*(speed_i-speed_j),Pij要向下取整。
由于L/speed_max是小数,不好搞。
可以分开商和余数来做,先忽略掉余数(只考虑整数部分)
例如3.2-1.5就变成3-1=2。
那么这样是会算多的,因为0.2-0.5<0,必须答案-1。
用树状数组存余数就可以解决这一问题。
speed_max≤1000000,余数是存的下的。
(速度从小到大排序)
【代码】
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=100100;
int n,len,sp[N];
ll T[1001000],ans,last,m,now;
void add(int x) { for(int i=x;i<=sp[n];i+=(i&-i)) T[i]++; }
ll query(int x)
{
ll yu=0ll;
for(int i=x;i;i-=(i&-i)) yu+=T[i];
return yu;
}
bool cmp(int A,int B) { return (A<B); }
int main()
{
freopen("2242.in","r",stdin);
freopen("2242.out","w",stdout);
scanf("%d%lld%d",&n,&m,&len);
for(int i=1;i<=n;i++) scanf("%d",&sp[i]);
sort(sp+1,sp+1+n,cmp);
last=0ll; ans=0ll;
for(int i=1;i<=n;i++)
{
ll q=m*sp[i]/sp[n];
ll p=m*sp[i]%sp[n];
last+=q;
now=q*i-last;
add(p+1);
now-=(i-query(p+1));
ans+=now;
}
printf("%lld\n",ans);
return 0;
}
第三题 T-shirt (cf183d)
【题目描述】
【解题思路】
50%
假设至少x人实际衬衫是k的概率是poss[k][x]
假设第1到第y个人中至少x人实际衬衫是k的概率是f[k][y][x]
f[k][y][x]=f[k][y-1][x-1] * pyk + f[k][y-1][x] * (1-pyk)
Poss[k][x] = f[k][n][x]
如果买x件衬衫,期望匹配数是sigma poss[k]i
可以发现,k一定时,poss[k]单调减小。而假设原来有x件k号衬衫,现在多买一件k号衬衫,那么第x+1件k号衬衫对答案的贡献是poss[k][x+1]
可以发现,答案是poss数组中,最大的n个数的和
时间复杂度O(N*N*M)
100%
利用单调性优化:如果poss[k][x]还未被选取,则必然不会选取poss[k][x+1].因此我们可以在选取了poss[k][x]后再计算poss[k][x+1]的值。
时间复杂度O(N*N+N*M)
【代码】
#include<cstdio>
#include<algorithm>
#define imax(a,b) (((a-b)>eps)?(a):(b))
using namespace std;
typedef long long ll;
const double eps=1e-6;
const int N=3010;
const int M=310;
int n,m,g[M];
double fn[2][M][N],p[N][M],poss[M][2],ans;
void letplay(int cl)
{
int yu=g[cl]&1^1;
poss[cl][yu]=0.0;
for(int i=1;i<=n;i++)
fn[yu][cl][i]=fn[yu^1][cl][i-1]*p[i][cl]+fn[yu][cl][i-1]*(1.0-p[i][cl]);
for(int i=0;i<=n;i++) fn[yu^1][cl][i]=0.0;
poss[cl][yu]=fn[yu][cl][n];
g[cl]^=1;
}
int main()
{
freopen("2243.in","r",stdin);
freopen("2243.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int a; scanf("%d",&a);
p[i][j]=1.0*a/1000;
}
for(int i=1;i<=m;i++)
{
g[i]=0;
for(int j=0;j<=n;j++) fn[0][i][j]=1.0;
letplay(i);
}
poss[0][0]=0.0; g[0]=0; ans=0.0;
for(int i=1;i<=n;i++)
{
int mx=0;
for(int j=1;j<=m;j++)
if(poss[j][g[j]&1]>poss[mx][g[mx]&1]) mx=j;
ans+=poss[mx][g[mx]&1];
letplay(mx);
}
printf("%.8lf\n",ans);
return 0;
}