2015-08-12NOIP模拟赛

时间:2021-12-17 10:07:12

2015-08-12NOIP模拟赛


考试,跪了……但是分析才是最重要滴。


第一题:

Description

"蓝猫淘气三千问,看蓝猫,我有姿势我自豪!"话说能考上SDFZ的孩纸们肯定都是很有姿势的孩纸们,但是大家普遍偏科,都只有一门科目考得好。已知SDFZ 的入学考试科目数量小于等于10^9,而有n 个学生参加了入学考试。现在SDFZ 要刷人了,招生办每一次刷人会把一个科目考得好的人全部刷掉,但是最多不能刷超过K 次。(刷就是不录取)而SDFZ 的校长看录取名单时,最喜欢看的就是连续都是同一个科目考得好的人。他定义完美学生序列为连续且考得好的科目都为同一门的学生序列。现在招生办主任想让你帮他设计一种录取方案,使得最长的完美学生序列尽量长。

Input

共N+1 行,第一行2 个正整数n,K,n 表示入学考试人数,K 表示刷人次数上限。
接下来N 行,每行仅一个正整数Ai,为 i 号学生所考得好的科目。

Output

仅1 个正整数,为最长的最长完美学生序列。

Sample Input

9 1
2
7
3
7
7
3
7
5
7

Sample Output

4

Sample Explanation

总共有9 个学生,最多只能刷一次学生。
若不刷,最长完美学生序列长度为2
若刷掉考第3 门考得好的学生,则学生序列变成2 7 7 7 7 5 7,最长完美学生序列长度为4.

Hint

10%的数据:n≤10;
30%的数据:n≤1000;
100%的数据:1≤n≤100000。

Analyse

很显然 30%的数据暴力枚举是可以通过的。
但是100%的数据怎么办呢?
这里我们采用类似于单调队列的方法:
用队列维护一个区间,使得这个区间的擅长科目数量不超过 K+1,那么统计出来的每个合法区间的众数的数量的最大值便为答案。具体操作如下:
(1).首先添加第一个元素入队列,此时 Head=Tail=1
(2).接下来不断添加元素入队,当队列内元素种类不大于 K+1 时统计答案。
(3).当队列里的元素种类大于 K+1 时,不断将队列头部的元素出队,直到队列内元素种类小于等于 K+1,这时要记得也要统计答案。
这便是这道题的算法。时间复杂度 O(n)。

第二题:

Description

此百团大战非彼百团大战也。这指的是SDFZ 的社团开始招人了。若若的LMZ现在站在操场上,有很多很多个社团在操场上排成一排。有些社团为了吸引人们加入,会表演节目。而现在LMZ 拿到了节目单,有n 个节目,其描述了在Ti 时刻Xi号社团会表演节目(持续时间忽略不计)。而LMZ 在一单位时间内最多也只能跑过V 个社团的距离(比如从1 号社团跑到V+1 号社团),而最少则可以不动,跑步的左右方向任意。他想知道:
1. 当他初始时刻是站在0 号社团的情况下,他最多能看到多少节目?
2. 当他初始时刻可以站在任意位置的情况下,他最多能看到多少节目?
注:初始时刻指的是时间为0。

Input

第一行仅1 个正整数n,为节目数量。
接下来n 行每行2 个正整数Xi,Ti,为第i 个节目的属性。输入数据保证不会有重复的节目。
最后一行一个正整数V,表示LMZ 的速度上限。

Output

仅2 个正整数,分别为问题1 和问题 2 的答案。

Sample Input

3
6 1
1 3
4 4
4

Sample Output

2 3

Sample Explanation

若一开始LMZ 站在0 号位置,那么在时间为1 时是不可能跑到6 号社团的,但是后面的2 个表演可以看得到;而若一开始便站在6 号社团处,便3 个表演都看得到。

Hint

10%的数据:n≤10;
40%的数据:n≤3000;
100%的数据:1≤n≤100000,-2*10^8≤Xi≤2*10^8,1≤V≤1000,1≤Ti≤10^6。

Analyse

40%,不说了,DP。
设 F[i]表示刚刚看到了第 i 场节目(即满足 T=Ti,X=Xi)的情况下最多看了多少节目。那么 F[i]=max(F[j]+1),其中 j 和 i 必须满足一下条件:
(1). Ti>Tj
(2). (Ti-Tj)*V≥|Xi-Xj|
至于两个询问的区别便是 dp 的初值不同,具体自己讨论,差别不大。时间复杂度 O(n^2)。
100%算法:显然,满足(2)就必须满足(1),因此条件(1)是无用的。那么我们把(2)拆开成如下两个不等式:
(3). Ti*V-Xi≥ Tj*V-Xj
(4). Ti*V+Xi≥Tj*V+Xj
注:这个地方不用讨论 Xi 与 Xj 的大小,因为|Xi-Xj|≥±(Xi-Xj)那么我们按照权值 Ti*V-Xi 从小到大排序以后,然后按照 Ti*V+Xi 这个权值去做最长不下降序列就行了。至于对于两个询问的初值依然自己研究,本题解不做探讨。
时间复杂度 O(nlog2n),用经典的二分求 LIS 解决问题。

第三题:

Description

你真的认为选课是那么容易的事吗?SDFZ 的ZY 同志告诉你,原来选课也会让人产生一种想要回到火星的感觉。假设你的一周有n 天,那么ZY 编写的选课系统就会给你n 堂课。但是该系统不允许在星期i 和星期i+1 的时候选第i 堂课,也不允许你在星期n 和星期一的时候选第n 堂课。然后连你自己也搞不清哪种选课方案合法,哪种选课不合法了。你只想知道,你到底有多少种合法的选课方案。

Input

有多组数据,请读到文件末结束。
对于每组数据仅一行,1 个正整数n。

Output

对于每组输出只有一行,1 个非负整数,为选课方案数量mod (10^9+7).

Sample Input

2
4

Sample Output

0
2

Sample Explanation

对于样例二:
周一上第二堂课,周二上第三堂课,周三上第四堂课,周四上第一堂课;
周一上第三堂课,周二上第四堂课,周三上第一堂课,周四上第二堂课。
共2 种选课方案。

Hint

第i 组数据:n≤10*i, 1≤i≤3;
100%的数据:1≤n≤100000。

Analyse

对于这题,我们可以把天和课建立节点利用Dinic计数暴力跑答案,也可以用容斥原理。
题解是这样的:
容斥原理,假设有 1 堂课所在的天数是错误的情况数是 W(1),则每个错误的课程 i 可以放在 i 或 i+1 天,剩下的选课方法数是(n-1)!。所以 w(1)=C(n,1)*2*(n-1)!,这一步很容易想到,有点坑的是 2 个以上的
情况,假设有 k 堂课所在的天数是错误的情况数是 W(K),总共 n 堂课分别记为 1,2,...n,它们可放的天数可以表示为(1,2)(2,3)(3,4)...(n,1)现在我们把括号去掉即得到一个数列 1,2,2,3,3,4,....n,1,现在我们从里面取出 K 个数,分别表示 k 堂课所在的天数,现在只要求出满足这个条件的取法数就可以了。
定理:假定数n个顶点沿一圆周排列,则从其中选取k个不相邻顶点的方法数是n/k*C(n-k-1,k-1)
证明:对于任意一个顶点A,先取A,然后再从不和A相邻的n-3个其他顶点中取k-1个不相邻顶点,显然可得到符合定理要求的组合,这个组合的个数为C((n-3)-(k-1)+1,k-1)=C(n-k-1,k-1)。一共有n个顶点,而且在每个组合中有k个元素,即可完成证明。
那么W(K)=(n-k)!*C(2n-k-1,k-1)*2n/k
有少数人会问为什么要乘(n-k)!,因为我们只选了K堂错的课,剩余(n-k)堂课可以随意选。
W(K)确定以后就是最裸的容斥原理了。
什么?容斥原理不会写?找老师要课件去吧,孩纸们。
在实现细节的时候,我们可以发现我们可以事先预处理好 n!,因此对于每组数据的时间复杂度为 O(n),即总时间复杂度为 O(nq),空间复杂度为 O(n)但是在实现细节的时候,若有人把时间复杂度写成 O(nlog2n*q)的话,就只有 60 分了。

但是通过打了一个长为30的表之后:
0, 0, 1, 2, 13, 80, 579, 4738, 43387, 439792, 4890741, 59216642, 775596313, 10927434464, 164806435783, 2649391469058, 45226435601207, 817056406224416, 15574618910994665, 312400218671253762, 6577618644576902053, 145051250421230224304, 3343382818203784146955, 80399425364623070680706, 2013619745874493923699123 …………  (部分)
我们通过观察发现a(i)  与 i*a(i-1) 之间有类似联系,经过推算:
a(n) = ((n^2-2*n)*a(n-1) + n*a(n-2) - 4(-1)^n)/(n-2) ( n >= 4.)
加上神奇的逆元大法(自行百度)
于是问题就迎刃而解了。


代码:
T1:
#include <iostream>
#include <vector>
#include <bitset>
#include <cmath>
#include <iomanip>
#include <string>
#include <string.h>
#include <time.h>
#include <memory.h>
#include <stdio.h>
#include <cmath>
#include <stdlib.h>
#include <algorithm>
#include <stack>
#include <queue>
#include <cstdio>
#include <climits>

#define intmin -2147483640
#define intmax 2147483640

using namespace std;

const int MAXN = 100010;

int N,M,K,A[MAXN],Ans=intmin,B[MAXN];
int Ne[MAXN],cnt;
int Q[MAXN],qin=0,qout=1;

int Binary (int x)
{
	int l=0,r=M,mid;
	while (l<=r)
	{
		mid=(l+r)>>1;
		if (B[mid]==x)  return mid;
		if (B[mid]<x)  l=mid+1;
		else  r=mid-1;
	}
	return -1;
}

void init ()
{
	scanf ("%d%d",&N,&K);
	int i,tmp;
	for (i=0;i<N;i++)
	scanf ("%d",&A[i]),B[i]=A[i];
	sort (B,B+N);
	tmp=B[N-1];
	unique (B,B+N);
	for (i=0;i<N;i++)
	if (B[i]==tmp) {M=i;break;}
	for (i=0;i<N;i++)
	A[i]=Binary(A[i]);
}

void work ()
{
     int i;
     for (i=0;i<N;i++)
     {
         Q[++qin]=A[i];
         if (!Ne[A[i]]) cnt++;
         Ne[A[i]]++;
         while (cnt>K+1)
         {
               int f=Q[qout];qout++;
               Ne[f]--;
               if (!Ne[f]) cnt--;
         }
         Ans=max(Ans,Ne[A[i]]);
     }
}

int main ()
{
    init ();
    work();
    cout<<Ans;
    return 0;
}
T2:
#include <iostream>
#include <vector>
#include <bitset>
#include <cmath>
#include <iomanip>
#include <string>
#include <string.h>
#include <time.h>
#include <memory.h>
#include <stdio.h>
#include <cmath>
#include <stdlib.h>
#include <algorithm>
#include <stack>
#include <queue>
#include <cstdio>
#include <climits>

#define intmin -2147483640
#define intmax 2147483640

using namespace std;

const int MAXN=100010;

typedef struct Activity_Node_Tp
{
    int t,x;
    int v1,v2;
    bool operator < (const Activity_Node_Tp &Y) const 
    {return (v1!=Y.v1)?(v1<Y.v1):(v2<=Y.v2);}
}act_;

act_ A[MAXN],B[MAXN];
int F[MAXN],Temp[MAXN],D[MAXN],Top,N,V;

int Binary (int x)
{
    int l=0,r=Top,mid;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(Temp[mid]>=x) r=mid-1;
        else
        {
            l=mid;
            if(Temp[l+1]>=x) return l+1;
            else l++;
        }
    }
}

void init ()
{
     scanf ("%d",&N);
     int i;
     for (i=1;i<=N;i++)
       scanf ("%d%d",&A[i].x,&A[i].t);
     scanf ("%d",&V);
     for (i=1;i<=N;i++)
       A[i].v1=A[i].t*V-A[i].x,
       A[i].v2=A[i].t*V+A[i].x;
     sort (A+1,A+N+1);
}

void clear ()
{for (int i=1;i<MAXN;i++)Temp[i]=intmax;}

void DP1 ()
{
     int i;
     Top=0;
     clear ();
     for (i=1;i<=N;i++)
     {
         if (A[i].v2>=Temp[Top] && !(Top==0 && V*A[i].t<abs(A[i].x)))
           Temp[++Top]=A[i].v2;
         else
         {
             int l=0,r=Top,mid;
             while (l<=r)
             {
                 mid=(l+r)>>1;
                 if(Temp[mid]<=A[i].v2) l=mid+1;
                 else r=mid-1;
             }
             if (!(l==1 && V*A[i].t<abs(A[i].x)))
             Temp[l]=A[i].v2;
         }
     }
     cout<<Top<<" ";
}

void DP2 ()
{
     int i;
     Top=0;
     clear ();
     for (i=1;i<=N;i++)
     {
         if (A[i].v2>=Temp[Top])
           Temp[++Top]=A[i].v2;
         else
         {
             int l=0,r=Top,mid;
             while (l<=r)
             {
                 mid=(l+r)>>1;
                 if(Temp[mid]<=A[i].v2) l=mid+1;
                 else r=mid-1;
             }
             Temp[l]=A[i].v2;
         }
     }
     cout<<Top<<" ";
}

int main()
{
    init ();
    DP1();
    DP2();
    return 0;
}

T3:
#include <iostream>
#include <vector>
#include <bitset>
#include <cmath>
#include <iomanip>
#include <string>
#include <string.h>
#include <time.h>
#include <memory.h>
#include <stdio.h>
#include <cmath>
#include <stdlib.h>
#include <algorithm>
#include <stack>
#include <queue>
#include <cstdio>
#include <climits>

#define intmin -2147483640
#define intmax 2147483640

using namespace std;

const int MAXN = 2000010;
const unsigned long long mod = 1000000007;


unsigned long long f[MAXN],ni[MAXN];

unsigned long long KSM (unsigned long long i,unsigned long long p)
{
     unsigned long long a=1,b=i,c=p;
     while (c)
     {
           if (c&1)
           a=a*b%mod;
           b=b*b%mod,c>>=1;
     }
     return a;
}

void nii ()
{
     unsigned long long i,j,k;
     for (i=1;i<=1000000;i++)
     ni[i]=KSM (i,mod-2);
     //for (i=1;i<=100000;i++)
     //cout<<ni[i]<<"  ";
}

int main ()
{
    //freopen ("ni.out","w",stdout);
    unsigned long long n;
    nii ();
    f[0]=0,f[1]=0,f[2]=0,f[3]=1,f[4]=2;
    for (unsigned long long i=5;i<=1000000;i++)
    {
    	f[i]=f[i-1]*i%mod;
    	if (i&1)
    	f[i]=(((i*i-2*i)*f[i-1]%mod+i*f[i-2]%mod+4)%mod)*(ni[i-2]%mod)%mod;
    	else
    	f[i]=(((i*i-2*i)*f[i-1]%mod+i*f[i-2]%mod-4+mod)%mod)*(ni[i-2]%mod)%mod;
	}
    while (~scanf ("%lld",&n))
    	printf ("%lld\n",f[n]);
    return 0;
}