【原创】2019.10.12模拟赛 NP问题 坏天平

Index

  • 2019.10.12模拟赛
    • 说在前面
    • NP问题
      • 问题描述
      • 输入格式
      • 输出格式
      • 输入输出样例1
        • 样例输入
        • 样例输出
        • 样例说明
      • 输入输出样例2
        • 样例输入
        • 样例输出
      • 样例说明
      • 数据规模与约定
      • 分析
    • 坏天平
      • 分析
      • 代码
    • 垃圾分类

2019.10.12模拟赛

说在前面

四年前,小白甜;
四年后,腹黑透。

orz NODGD。

NP问题

问题描述

p6pou在平面上画了 n n n个点,并提出了一个问题,称为 N − P o i n t s N-Points NPoints问题,简称NP问题。
p6pou首先在建立的平面直角坐标系,并标出了这 n n n个点的坐标。这 个点的坐标都是正整数,任意三个点都不共线。然后,p6pou选择其中一个点 A A A ,画一条 y y y轴的平行线,这条直线称为 l l l。直线 l l l以点 A A A为旋转中心逆时针旋转,当直线 l l l碰到另外一个点 B B B时,就立刻将 B B B点作为新的旋转中心继续逆时针旋转。此后,每当直线 l l l碰到除了旋转中心以外的另一个点,都会将这个点作为新的旋转中心继续逆时针旋转。这个过程可以一直进行。
p6pou不太关心旋转的完整过程,只想知道,当 旋转至平行于 轴时直线方程有哪些可能。

输入格式

输出文件 n p . i n np.in np.in
第一行输入两个整数 n , A n,A n,A,表示平面上共有 n n n 个点,一开始 l l l y y y轴平行,直线方程是 x = A x=A x=A
1 1 1到第 n n n行中,第 i i i行两个正整数 x i , y i x_i,y_i xi,yi,表示编号为 i i i的点的坐标,保证任意三点不共线。

输出格式

输出文件 n p . o u t np.out np.out
直线 l l l旋转到与 x x x轴平行时方程是 y = B y=B y=B,按从小到大的顺序输出 B B B所有可能的值,每行输出一个数。

输入输出样例1

样例输入

4 1
1 2
2 1
3 2
2 3

样例输出

1
3

样例说明

【原创】2019.10.12模拟赛 NP问题 坏天平_第1张图片

输入输出样例2

样例输入

6 2
2 2
2 4
4 1
4 2
3 4
1 3

样例输出

2
3
4

样例说明

【原创】2019.10.12模拟赛 NP问题 坏天平_第2张图片
【原创】2019.10.12模拟赛 NP问题 坏天平_第3张图片

数据规模与约定

对于100%的数据,n<=200 。

分析

这个包里三个700MB的视频,2个G的样例包,差点没吓死我。
于是我录了个gif挂在上面了。

我的做法啊……
【原创】2019.10.12模拟赛 NP问题 坏天平_第4张图片
以上是考场分析。

因为我相信数学是美丽而优雅的,所以我只dfs了两圈,就是从A点出发以后,第二次回来的话就结束程序。
于是就90分了。
如果我跑4圈就能A。
最好跑n圈,反正时间复杂度够,n才两百。
所以为什么n才两百呢?

【原创】2019.10.12模拟赛 NP问题 坏天平_第5张图片

于是我寻思着你造数据的时候就造一个大圆大椭圆大抛物线,在上面取点呗,你题面不写保证所有点在一个圆上,也没人会针对你的数据投机取巧,也不至于水到哪里去。

还是那句话,不喜欢冠冕堂皇以暴力为正解的题目。

所以简单说几句正解。为什么说 l l l两边的点的个数不变呢?
因为你旋转的时候,撞到了一个点。假设在撞之前,左边有L个,右边有R个,线上只有1个是我原来的旋转中心A,而我新撞到的点是点B,假设A在B左边,那么在撞到你B的前一瞬间,你B在我右边,你现在到线上了,R–,但我A出来了,而且我A不仅出来了还在你l的右边(画图自己看),所以R++。什么也没有发生。

所以我开始竖着的时候的L,R和我在任意时刻的L,R是不会变的。
而可能出现的横着的l就那么n个。
所以排排序就解决了。

#include
#include
#include
#include
#include
using namespace std;
 
inline void Read(int &p)
{
    p=0;
    char c=getchar();
    while(c<'0' || c>'9') c=getchar();
    while(c>='0' && c<='9')
        p=p*10+c-'0',c=getchar();
}
 
const int MAXN=444;
const double pi=acos(-1.0);
 
struct node
{
    int id;
    double alpha;
    bool operator < (const node &n) const
    {
        return alpha<n.alpha;
    }
}arr[MAXN][MAXN];
int n,A,c,B[MAXN],x[MAXN],y[MAXN],ok[MAXN],cnt[MAXN],ans[MAXN];
 
double angel(int i,int j)
{
    double ans=(x[i]==x[j])?pi/2.0:atan((1.0*y[i]-y[j])/(x[i]-x[j]));
    if(ans<0) ans+=pi;
    return ans;
}
 
void dfs(int now,int las,int ed,int all)
{
    if(now==ed) all++;
    if(all==n) return ;
    int pos=(upper_bound(arr[now]+1,arr[now]+1+cnt[now],(node){now,angel(now,las)})-arr[now]);
    if(arr[now][pos].alpha>=pi) ok[now]=1;
    dfs(arr[now][pos].id,now,ed,all);
}
 
int main()
{
    Read(n),Read(A);
    for(int i=1;i<=n;i++) Read(x[i]),Read(y[i]);
    for(int i=1;i<=n;i++)
    {
        if(x[i]==A) B[++c]=i;
        for(int j=1;j<=n;j++)
            if(i!=j)
            {
                arr[i][++cnt[i]].id=j,arr[i][cnt[i]].alpha=angel(i,j);
                if(arr[i][cnt[i]].alpha<pi) arr[i][++cnt[i]].id=j,arr[i][cnt[i]].alpha=arr[i][cnt[i]-1].alpha+pi;
            }
        sort(arr[i]+1,arr[i]+1+cnt[i]);
    }
 
    for(int i=1;i<=c;i++) dfs(B[i],B[i],B[i],0);
         
    for(int i=1;i<=n;i++) if(ok[i]) ans[++ans[0]]=y[i];
    sort(ans+1,ans+1+ans[0]);
    int las=ans[1]; printf("%d\n",las);
    for(int i=2;i<=ans[0];i++) if(ans[i]!=las) printf("%d\n",ans[i]),las=ans[i];
}

坏天平

【原创】2019.10.12模拟赛 NP问题 坏天平_第6张图片
【原创】2019.10.12模拟赛 NP问题 坏天平_第7张图片
【原创】2019.10.12模拟赛 NP问题 坏天平_第8张图片

分析

我们做的是题面5.0的,以下是题面4.0的内容。
【原创】2019.10.12模拟赛 NP问题 坏天平_第9张图片
题面5.0就直接告诉我们m没有任何用了。

我在考场上做了什么呢?
【原创】2019.10.12模拟赛 NP问题 坏天平_第10张图片

我做的事情就是算n=1的时候的答案。
题目残酷地告诉我,你辛苦求的 f [ i ] f[i] f[i]居然是 C i ⌊ i 2 ⌋ \large C_{i}^{\lfloor \frac{i}{2}\rfloor} Ci2i
在这里插入图片描述


先说说我的f[i]是在搞什么。
首先跳过无数错误的尝试。
【原创】2019.10.12模拟赛 NP问题 坏天平_第11张图片
g ( x , y ) g(x,y) g(x,y)代表左边放x个,右边放y个的方案数,显然 g ( x , y ) = g ( x , y − 1 ) + [ x − 1 > = y ] ∗ g ( x − 1 , y ) g(x,y)=g(x,y-1)+[x-1>=y]*g(x-1,y) g(x,y)=g(x,y1)+[x1>=y]g(x1,y)。然后 f [ n ] = ∑ i = 1 ⌊ n + 1 2 ⌋ g ( i , n − i + 1 ) f[n]=\sum _{i=1}^{\lfloor\frac{n+1}{2}\rfloor}g(i,n-i+1) f[n]=i=12n+1g(i,ni+1)
所以我说这是一个畸形的杨辉三角:
【原创】2019.10.12模拟赛 NP问题 坏天平_第12张图片
于是我就愉快地得出了 f [ n ] f[n] f[n]的前面几十项。
于是愉快的发现了规律 f [ 2 n ] = 2 × f [ 2 n − 1 ] , f [ 2 n + 1 ] = 2 × f [ 2 n ] − C a t a l a n ( n ) f[2n]=2\times f[2n-1],f[2n+1]=2\times f[2n]-Catalan(n) f[2n]=2×f[2n1],f[2n+1]=2×f[2n]Catalan(n)


我的式子的正确性不浪费时间讨论了,问题是题解给的 C i ⌊ i 2 ⌋ \large C_{i}^{\lfloor \frac{i}{2}\rfloor} Ci2i是怎么来的。

回到—— g ( x , y ) 代 表 左 边 放 x 个 , 右 边 放 y 个 的 方 案 数 , g ( x , y ) = g ( x , y − 1 ) + [ x − 1 > = y ] ∗ g ( x − 1 , y ) g(x,y)代表左边放x个,右边放y个的方案数,g(x,y)=g(x,y-1)+[x-1>=y]*g(x-1,y) g(x,y)xyg(x,y)=g(x,y1)+[x1>=y]g(x1,y)。然后 f [ n ] = ∑ i = n + 1 2 n g ( i , n − i + 1 ) f[n]=\sum _{i=\frac{n+1}{2}}^{n}g(i,n-i+1) f[n]=i=2n+1ng(i,ni+1)——这一步。

假设没有这个左比右大的条件,我们的答案可以在图上表示为(请忽略橙色的线):
【原创】2019.10.12模拟赛 NP问题 坏天平_第13张图片
就是我们从 ( 0 , 0 ) (0,0) (0,0)出发,每次横坐标或纵坐标 + 1 +1 +1,最后到达 x + y = n x+y=n x+y=n这条线上某点的方案数,到达 ( x 0 , y 0 ) (x_0,y_0) (x0,y0)就是这个点的方案数(就是我 C n x 0 = C n y 0 C_{n}^{x_0}=C_{n}^{y_0} Cnx0=Cny0哒,至于为什么?快用杨辉三角。你也可以这么理解,总共2n步,你从中选出n步填上向右/上走),然后把这些点全加起来就能得到 2 n 2^n 2n,这就是每一步从 x + 1   o r   y + 1 x+1 ~or~ y+1 x+1 or y+1中乱选一个的方案数。(这也许可以拿来证明二项式定理?)

那么本题的这个“左>=右”即“x>=y”的条件在图上怎么表现?它就是在说,你在走路的时候,不能越过 y = x y=x y=x这个蓝线,也就是说,图中的红色是不合法的了,而绿色是合法的。
【原创】2019.10.12模拟赛 NP问题 坏天平_第14张图片

这是什么,这有卡特兰数内味了。


我们卡特兰数是怎么做的?
【原创】2019.10.12模拟赛 NP问题 坏天平_第15张图片
我们要从 ( 0 , 0 ) (0,0) (0,0)走到 ( n , n ) (n,n) (n,n),而且走的时候不能超过 y = x y=x y=x这条线,问方案数。
我们知道,不设要求乱走,我们的方案数是 C 2 n n C_{2n}^{n} C2nn这么多,但加上了要求,要减去一些不合法的方案。不合法是什么意思?就是它超出了 y = x y=x y=x,就是它走到了 y = x + 1 y=x+1 y=x+1上。
【原创】2019.10.12模拟赛 NP问题 坏天平_第16张图片
我们把不合法路径在越过 y = x y=x y=x,来到 y = x + 1 y=x+1 y=x+1之后的路径关于 y = x + 1 y=x+1 y=x+1对称,容易发现,求终点为 ( n , n ) \bold{(n,n)} (n,n),中途越过了 y = x \bold{y=x} y=x的方案数就等于终点为 ( n − 1 , n + 1 ) \bold{(n-1,n+1)} (n1,n+1),没有限制条件的方案数,即 C 2 n n − 1 = C 2 n n + 1 C_{2n}^{n-1}=C_{2n}^{n+1} C2nn1=C2nn+1
所以就得到卡特兰数 C a t a l a n ( n ) = C 2 n n − C 2 n n − 1 Catalan(n)=C_{2n}^{n}-C_{2n}^{n-1} Catalan(n)=C2nnC2nn1,后续的化简就省略了。


那么对于本题的这个情况,它和卡特兰数的区别在哪里?
它同样有一条不可跨越的线 y = x y=x y=x,同样从 ( 0 , 0 ) (0,0) (0,0)出发——对了,终点不同,卡特兰数的终点是 ( n , n ) (n,n) (n,n),而它的终点则不止一个, ( n , n ) (n,n) (n,n), ( n + 1 , n − 1 ) (n+1,n-1) (n+1,n1)…… ( 2 n , 0 ) (2n,0) (2n,0)
终点是 ( n , n ) (n,n) (n,n)的情况的方案数就是 C a t a l a n ( n ) = C 2 n n − C 2 n n + 1 Catalan(n)=C_{2n}^{n}-C_{2n}^{n+1} Catalan(n)=C2nnC2nn+1,那么 ( n + 1 , n − 1 ) (n+1,n-1) (n+1,n1)呢?
如果不加限制,我们的方案数就是在一个长为 2 n 2n 2n的操作序列里面选 n + 1 / n − 1 n+1/n-1 n+1/n1个格子填上向右走/向左走的方案数,就是 C 2 n n + 1 = C 2 n n − 1 C_{2n}^{n+1}=C_{2n}^{n-1} C2nn+1=C2nn1
对于那些超过了线的,我们把它的从第一处超过线的位置开始的部分关于 y = x + 1 y=x+1 y=x+1对称,就得到一条从 ( 0 , 0 ) (0,0) (0,0)自由自在无忧无虑走到 ( n − 2 , n + 2 ) (n-2,n+2) (n2,n+2)的路线。总共多少条呢,与上文类似的, C 2 n n − 2 = C 2 n n + 2 C_{2n}^{n-2}=C_{2n}^{n+2} C2nn2=C2nn+2
所以说终点为 ( n + 1 , n − 1 ) (n+1,n-1) (n+1,n1)的答案是 C 2 n n − 1 − C 2 n n − 2 C_{2n}^{n-1}-C_{2n}^{n-2} C2nn1C2nn2
敏锐的读者、数学功底无限好的你们一定发现了顶好的规律,你把这些个终点的答案一加起来就得到了 f [ 2 n ] = C 2 n n − C 2 n 0 = C 2 n n f[2n]=C_{2n}^{n}-C_{2n}^0=C_{2n}^{n} f[2n]=C2nnC2n0=C2nn

太棒了!现在我们知道n=1时的答案了,这样我们就得到了——零分!可喜可贺,可喜可贺。
(出题人,你好狠)

不过n>1的思路在我的那张便签里也有体现,本人,虽然智力障碍,但还是勇敢地试图递推!
在这里插入图片描述
【原创】2019.10.12模拟赛 NP问题 坏天平_第17张图片
以上是题解。

数学功底无限差的我还需要要绕一大圈才能明白,跟我一起绕圈圈吧。
它这个递推和常规的递推有些不同,我们(也许只有我)一般是考虑把第n种物品加入,然后由 d p n − 1 dp_{n-1} dpn1推出 d p n dp_n dpn。这道题则是把最轻的砝码拿了出来。

现在我们求 a n s n ans_n ansn,我们假设我们没有 m 0 m^0 m0这种砝码,只用 m 1 , m 2 , ⋯   , m n − 1 m^1,m^2,\cdots,m^{n-1} m1,m2,,mn1 n − 1 n-1 n1种砝码能摆出的方案数是多少? a n s n − 1 。 ans_{n-1}。 ansn1
现在我们把 m 0 m^0 m0插入这些方案中。因为它是最轻的,如果我们把 m 0 m^0 m0放在 m 1 , m 2 , ⋯   , m n − 1 m^1,m^2,\cdots,m^{n-1} m1,m2,,mn1之中,我们可以随便摆,爱咋摆咋摆。所以我们设有 i i i m 0 m^0 m0摆在了大部队之前,它能贡献的方案数就是 C i i / 2 ( 整 除 ) C_{i}^{i/2}(整除) Cii/2()。那么剩下 k − i k-i ki个呢?现在就是一个插板的经典问题了。这篇blog的T1就详细地解释了这个问题。 自己看去。

除去开头的一个,总共 k ∗ ( n − 1 ) = k ∗ n − k k*(n-1)=k*n-k k(n1)=knk个间隔。你要把 k − i k-i ki个板子插进去,而且每个间隔可以塞多个板子。
我们可以把这个的方案数看作方程 x 1 + x 2 + ⋯ + x k − i = k ∗ n − k x_1+x_2+\cdots+x_{k-i}=k*n-k x1+x2++xki=knk的非负整数解的个数。
于是我们为了规避某个 x = 0 x=0 x=0,即某个间隔上出现了多个板子的情况出现,我们把所有 x + + x++ x++,变成 x 1 + x 2 + ⋯ + x k − i = k ∗ n − k + k − i = k ∗ n − i x_1+x_2+\cdots+x_{k-i}=k*n-k+k-i=k*n-i x1+x2++xki=knk+ki=kni的正整数解的个数,在 k ∗ n − i k*n-i kni个间隔里面选 k − i k-i ki个出来插板,这是什么啊,这是 C k ∗ n − i k − i C_{k*n-i}^{k-i} Ckniki

每次插入时,你都可以任选放在左边还是右边,所以再乘上 2 k − i 2^{k-i} 2ki

然后枚举 i i i
然后递推推起来。
时间复杂度 Θ ( n k ) \Theta(nk) Θ(nk)

我的数学怎么这么差。

代码

#include
#include
#include
#include
#include
using namespace std;
 
inline void Read(int &p)
{
    p=0;
    char c=getchar();
    while(c<'0' || c>'9') c=getchar();
    while(c>='0' && c<='9')
        p=p*10+c-'0',c=getchar();
}
 
const int MAXN=10002030,mod=998244353;
int n,m,k,inv[MAXN],arkcpy[MAXN];
 
int main()
{
    inv[0]=inv[1]=arkcpy[0]=1;
    Read(n),Read(m),Read(k);
    for(int i=2;i<=k;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(int i=1;i<=k;i++) arkcpy[i]=1ll*arkcpy[i-1]*i%mod*inv[(i+1)>>1]%mod;
    long long ans=arkcpy[k];
    for(int i=2;i<=n;i++)
    {
        long long t=0,c=1;
        for(int j=0;j<=k;j++) (t+=1ll*c*arkcpy[k-j])%=mod,(c*=2ll*((i-1)*k+j)%mod*inv[j+1]%mod)%=mod; 
        (ans*=t)%=mod;
    }
    cout<<ans<<endl;
}

垃圾分类

这道题真的是垃圾分类,我被分出来了。我是有害垃圾,应当深挖掩埋焚烧隔离。
没想,没写,没打暴力,没看题解。

(其实我看了)

你可能感兴趣的:(#,题目,#,☠☠☠☠☠哼本人已死亡,#,☆☆★★★哦有点难度呢)