「题解」「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}\).
题解
一道崩坏三观的好题。
对于这种给定公式的题,我们解题的常规套路是:
- 分析题目给出的数学公式,找到 \(a,b,c\) 和 \(x_i\) 的对应关系;
- 用数据结构维护这一关系,用很快的速度完成匹配;
然而这一道题,将式子打开之后,你会发现似乎并没有传说中的什么“数学关系”,有的只是你的无限自闭。
那么怎么做?关注到题目中“所有的询问都将会按上述方式进行加密,包括标志着询问的结束的那个询问也是这样。”扯一句话。
什么意思?假如说它的询问一共有 \(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}\) 表示的是解密之后的第 \(Q-1\) 组的 \(a\) 的真实的值,\(b,c\) 同理。
根据题目加密规则,有 \(a_{1,Q-1}=a_{0,Q-1}+lastans\),而 \(b,c\) 同理。
那么我们将上式中的 \(a_{1,Q-1}\) 以及 \(b,c\) 替换,可以得到
这个式子中,我们仔细观察,可以知道只有 \(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;
}