「题解」「NOIP模拟赛」一道防AK好题

题目

题目描述

\(\text{Lemon}\) 认为在第一届 \(『\text{Citric}』\) 杯模拟赛中出的题目太简单了,于是他决定,这次要给参赛选手们一个下马威! ^_^

\(\text{Lemon}\) 手上有一个长度为 \(n\) 的数列,第 \(i\) 个数为 \(x_i\)

他现在想知道,对于给定的 \(a,b,c\),他要找到一个 \(i\),使得 \(a(i+1){x_i}^2+i(b+1)x_i+(c+i)=0\) 成立。

如果有多个 \(i\) 满足,\(\text{Lemon}\) 想要最小的那个 \(i\)

\(\text{Lemon}\) 有很多很多组询问需要你回答,多到他自己也不确定有多少组。所以在输入数据中 \(a=b=c=0\) 标志着 \(\text{Lemon}\) 的提问的结束。

更加糟糕的是,\(\text{Lemon}\) 为了加大难度,决定对数据进行加密以防止离线算法的出现。

假设你在输入文件中读到的三个数为 \(a_0,b_0,c_0\),那么 \(\text{Lemon}\) 真正要询问的 \(a=a_0+lastans,b=b_0+lastans,c=c_0+lastans\).

\(lastans\) 的值是你对 \(\text{Lemon}\) 的前一个询问的回答。如果这是第一个询问,那么 \(lastans=0\).

所有的询问都将会按上述方式进行加密,包括标志着询问的结束的那个询问也是这样。

为提高读入效率,请用 scanf(),而不能用 cin,即使加 sync_with_stdio(false) 也不行!

对于不确定数量的数据,当读入完最后一行数据后,再次调用 scanf(),会返回 \(-1\),以此可判断读入是否结束。

输入格式

输入文件第一行包含一个正整数 \(n\),表示数列的长度。

输入文件第二行包含 \(n\) 个整数,第 \(i\) 个数表示 \(x_i\) 的值。

接下来若干行,每行三个数,表示加密后的 \(a,b,c\) 值(也就是上文所述的 \(a_0,b_0,c_0\))。

输出格式

包含若干行,第i行的值是输入文件中第i个询问的答案。注意,你不需要对标志着询问结束的那个询问作答。

同时,标志着询问结束的询问一定是输入文件的最后一行。也就是,输入文件不会有多余的内容。

样例

样例输入

5
-2 3 1 -5 2 
-5 -4 145
-1 -6 -509
-9 -14 40
-3 -13 21
-3 -3 -3

样例输出

5
4
3
3

数据范围与提示

第一个询问中,真实的 \(a=-5+0=-5,b=-4+0=-4,c=145+0=145\)(第一个询问中 \(lastans=0\)

带入发现,\(i=5\) 时,\(-5\times (5+1)\times 2^2+(-4+1)\times 5\times 2+145+5=0\),而其他的i均不符合条件。所以答案是 \(5\).

第二个询问中,真实的 \(a=-1+5=4,b=-6+5=-1,c=-509+5=-504\)\(lastans\) 是上一个询问的答案的值,也就是 \(5\)

经带入发现,\(i=4\) 时,\(4\times (4+1)\times (-5)^2+(-1+1)\times 4\times (-5)+(-504)+4=0\),满足条件,而其他的i均不满足条件,所以答案是4.

同理,第三个询问中真实的 \(a=-5,b=-10,c=44\).答案 \(i=3\).

第四个询问中真实的 \(a=0,b=-10,c=24\),答案 \(i=3\).

第五个询问中真实的 \(a=0,b=0,c=0\),此时我们发现这是一个标志着结束的询问,这个询问我们无需作出回答。

对于 \(40\%\) 的数据,满足 \(N\le 1000\),需要作出回答的询问个数不超过 \(1000\).

对于 \(100\%\) 的数据,满足 \(N\le 50000\),需要作出回答的询问个数不超过 \(500000\)\(x_i\) 的绝对值不超过 \(30000\),解密后的 \(a\) 的绝对值不超过 \(50000\),解密后的 \(b\) 的绝对值不超过 \(10^8\),解密后的 \(c\) 的绝对值不超过 \(10^{18}\).

题解

一道崩坏三观的好题。

对于这种给定公式的题,我们解题的常规套路是:

  1. 分析题目给出的数学公式,找到 \(a,b,c\)\(x_i\) 的对应关系;
  2. 用数据结构维护这一关系,用很快的速度完成匹配;

然而这一道题,将式子打开之后,你会发现似乎并没有传说中的什么“数学关系”,有的只是你的无限自闭。

那么怎么做?关注到题目中“所有的询问都将会按上述方式进行加密,包括标志着询问的结束的那个询问也是这样。”扯一句话。

什么意思?假如说它的询问一共有 \(Q\) 个(包括了结束的那组 \(a,b,c\)),那么,因为解密之后的最后一组是 \(0,0,0\) ,也就是说,第 \(Q-1\) 组询问的答案我们是知道的。

现在我们再从头分析,我们知道了 \(Q\)\((a_0,b_0,c_0)\) 的值,以及第 \(Q-1\) 组的答案,不妨设最后一组答案为 \(i\) ,根据公式,我们可以把第 \(Q-1\) 组的询问的等式写出来,即

\[a_{1,Q-1}(i+1){x_i}^2+i(B_{1,Q-1}+1)x_i+c_{1,Q-1}+i=0 \]

其中,\(a_{1,Q-1}\) 表示的是解密之后的第 \(Q-1\) 组的 \(a\) 的真实的值,\(b,c\) 同理。

根据题目加密规则,有 \(a_{1,Q-1}=a_{0,Q-1}+lastans\),而 \(b,c\) 同理。

那么我们将上式中的 \(a_{1,Q-1}\) 以及 \(b,c\) 替换,可以得到

\[(a_{0,Q-1}+lastans)(i+1){x_i}^2+i(B_{0,Q-1}+lastans+1)x_i+c_{0,Q-1}+lastans+i=0 \]

这个式子中,我们仔细观察,可以知道只有 \(lastans\) 是未知的,那么我们可以直接解得第 \(Q-2\) 组的答案,并将其记作 \(i\) ,然后去解第三组......最后倒退将所有的解完即可。

/*
* @Author: Arextre
* @Date:   2020-03-03 09:44:57
* @Last Modified by:   Arextre
* @Last Modified time: 2020-03-03 16:15:57
*/
#include<cstdio>

#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
#define LL long long
#define ull unsigned long long
#define uint unsigned int
#define pii pair< int,int >
#define Endl putchar('\n')
#define int long long
// #define int unsigned
// #define int unsigned long long

#define cg (c=getchar())
template<class T>inline void qread(T& x){
    char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
inline int qread(){
    int x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return f?-x:x;
}
#undef cg
// template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
    inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
template<class T>void fwrit(const T x){
    if(x<0)return (void)(putchar('-'),fwrit(-x));
    if(x>9)fwrit(x/10);
    putchar(x%10^48);
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
    return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}

const int MAXQ=500000;
const int MAXN=50000;

int x[MAXN+5],n,q=1;

int a[MAXQ+5],b[MAXQ+5],c[MAXQ+5];

int ans[MAXQ+5];

inline void Init(){
    n=qread();
    rep(i,1,n)x[i]=qread();
    while(~scanf("%lld %lld %lld",&a[q],&b[q],&c[q]))++q;
    --q;
}

inline void solve(const int t,const int i){//通过 t 算 t-1 的答案
    int A=(i+1)*x[i]*x[i],B=i*x[i],C=c[t]+i;
    ans[t-1]=(-A*a[t]-B*b[t]-B-C)/(A+B+1);
}

signed main(){
    Init();
    // rep(i,1,q)printf("%lld %lld %lld\n",a[i],b[i],c[i]);
    ans[q-1]=-a[q];--q;
    int T=q;
    while(q>1)solve(q,ans[q]),--q;
    rep(i,1,T)writc(ans[i],'\n');
    return 0;
}
posted @ 2020-03-04 08:27  Arextre  阅读(419)  评论(0编辑  收藏  举报