【NOIP模拟赛】 思维+离线+递推 数列(好题)

来源:互联网 发布:网络渗透 pdf 编辑:程序博客网 时间:2024/05/20 14:16


    潜水多年重新上岸冒个泡……

    PS:注意这道题的时间限制为2s(很重要!!!)

    不得不说,这确实是一道防AK的好题——见这道题的题解(多年以前的)


    相信很多人一看就会觉得这是一道暴力题,因为数据加了密所以根据随机性n^2算法能跑出nlogn,就算不能还有我们很重要的2s时限扛着呢是吧?然而这2s时限并没有什么用处233333什么?我说过这很重要吗这道题确确实实给你搞了若干组暴力会TLE的数据。

    我相信你可能会想着分治或高级数据结构,甚至会想到分块来解决这道题。我不知道是否有这样的方法,但是这些显然都不是正解。我们注意到这道题其实有很多提示的地方的。

    1.这个方程式

    我猜你会想着这很有规律,能不能把它用数学方法转化一下神马的。

    但我想说,这……并没有什么很明显的规律(至少我没有看出来)

    正是这个没有规律的性质,让我们想到不能用一般方法做这道题

    2.对离线的封杀

    这其实是欲擒故纵之招,表面上不能离线做,因为没有上一个答案lastans,你根本就不知道a、b、c。但正是这一点误导了许多人,我们总想着用a、b、c算ans,其实没有必要。这道题如果不加密,你好像也没有什么离线的方法去做吧?(<- 一语道破天机)加密就是让你往离线方面想,他其实就是一道离线题。至于为什么,请看下面一条。

    3.被加密的end语句

    这道题另一个很重要的提示在于,他为什么不采用读入询问个数的方式输入,而是读入到0 0 0结束。而且就算是0 0 0结尾,也不至于把0 0 0给加密了。相信很多人会发现,其实他把最后一个询问的答案告诉你了!我们如果抓住这一条性质,把所有的ans给推出来,不就好了吗?

    这看起来很不可思议,但我们还是看看能不能通过最后一个询问和a0、b0、c0,得到倒数第二个询问的答案。

    设a=a0+k,b=b0+k,c=c0+k,最后一个答案为i

    原始可以化为

    (a0+k)*(i+1)*X[i]^2+(b0+k+1)*i*X[i]+c0+k+i=0

    将式子拆开得到

   (a0*(i+1)*X[i]^2+(b0+1)*i*X[i]+c0+i)+k*((i+1)*X[i]^2+i*X[i]+1)=0

    我们发现,左边这一大坨包括a0,b0,c0,i,X[i],全是已知量,记为s

    而右边k的系数也全是已知量,记为p

    移一下项得到

    k=-s/p

    这个k便是倒数第二个询问的答案!同理,我们也可以倒着推出所有答案!

    这道题虽然十分惊艳,但是我们也是可以找到像这样倒着做的题的,例如下面这道题,就需要倒着做,把删边变成增边,便可以用并茶集来做了!


    所以我要给代码了!

#include<queue>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define N 50005#define M 500005#define ll long longint n,ans[M];ll ask[M][3],A[N];int main(){//freopen("seq.in","r",stdin);//freopen("seq.out","w",stdout);scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%lld",&A[i]);int m=1;while(scanf("%lld%lld%lld",&ask[m][0],&ask[m][1],&ask[m][2])==3)m++;m--;ans[m-1]=-ask[m][0];for(int i=m-1;i>1;i--){ll a=ask[i][0],b=ask[i][1],c=ask[i][2];int l=ans[i];ll s=a*(l+1)*A[l]*A[l]+(b+1)*l*A[l]+c+l;ll p=l*A[l]+(l+1)*A[l]*A[l]+1;ans[i-1]=-s/p;}for(int i=1;i<m;i++)printf("%d\n",ans[i]);}