洛谷10月21日模拟赛解题报告
~~考试时太弱,T1+T3一共只拿了暴力分60分,第二题看都没看一眼(其实就是一个二分答案+贪心早知道去做了)~~
T1:浮游大陆的68号岛
题目大意就是有n个点,给出每个点到前一个点的距离和每个点的值,然后有m个询问,每次询问从l到r这个区间的每个点到x点的距离*点值的总和模19260817。
Solution:
1、首先数据很大,直接暴力的话只能拿到30分到50分(关键看你怎么打暴力),正解其实是前缀和+同余定理(考试时想到了前缀和,但是没有继续深度思考,打了一个前缀和+树状数组其实就是一个暴力)。
2、首先乘法和加法是满足同余定理的,那么我们可以用两个前缀和数组分别表示到第i个点的距离和到第i个点的要求的值的总和(当然要取模),同理从后往前预处理出两个后缀和,每次查询时只需要用前面到x的值加上后面到x的值取模就好了。时间复杂度O(n),代码量其实很少、思维量也不大,貌似说的不太清楚,去看注释后的代码:
1 #include<bits/stdc++.h> 2 #pragma GCC optimize(2) 3 using namespace std; 4 #define ll long long 5 #define il inline 6 #define mod 19260817 7 #define maxn 200005 8 ll n,m,dis[maxn],w[maxn],fall[maxn],ball[maxn],fsum[maxn],bsum[maxn],ans[maxn]; 9 //dis数组存距离,w数组存点值,fall数组存从前往后的距离和,ball数组存从后往前的距离和,fsum存从前往后的权值和,bsum存从后往前的权值和 10 il ll gi() //读入优化 11 { 12 ll a=0;char x=getchar();bool f=0; 13 while((x<'0'||x>'9')&&x!='-')x=getchar(); 14 if(x=='-')x=getchar(),f=1; 15 while(x>='0'&&x<='9')a=a*10+x-48,x=getchar(); 16 return f?-a:a; 17 } 18 int main() 19 { 20 n=gi(),m=gi(); //下面的4个循环即上面所说的4个数组的预处理 21 for(int i=1;i<n;i++)dis[i+1]=gi(),dis[i+1]=(dis[i+1]+dis[i])%mod; 22 for(int i=1;i<=n;i++)w[i]=gi(); 23 for(int i=2;i<=n;i++)fsum[i]=(fsum[i-1]+dis[i]*w[i]%mod)%mod; 24 for(int i=n-1;i>=1;i--)bsum[i]=(bsum[i+1]+(dis[n]-dis[i]+mod)*w[i]%mod)%mod; 25 for(int i=1;i<=n;i++)fall[i]=(w[i]+fall[i-1])%mod; 26 for(int i=n;i>=1;i--)ball[i]=(ball[i+1]+w[i])%mod; 27 ll x,l,r; 28 while(m--) 29 { 30 x=gi(),l=gi(),r=gi(); //输出的判断大家模拟一遍应该都能理解,我这里解释一下相减后为什么要+mod,因为对前缀和取模后不一定满足单调性,所以可能出现负值,加上mod便能化解这个问题 31 if(x<=l)printf("%lld\n",(fsum[r]-fsum[l-1]+mod-dis[x]*(fall[r]-fall[l-1]+mod)%mod+mod)%mod); 32 else if(x>=r)printf("%lld\n",(bsum[l]-bsum[r+1]+mod-(dis[n]-dis[x]+mod)*(ball[l]-ball[r+1]+mod)%mod+mod)%mod); 33 else printf("%lld\n",(bsum[l]-bsum[x+1]+mod-(dis[n]-dis[x]+mod)*(ball[l]-ball[x+1]+mod)%mod+mod+fsum[r]-fsum[x-1]+mod-dis[x]*(fall[r]-fall[x-1]+mod)%mod+mod)%mod); 34 } 35 return 0; 36 }
PS:~蒟蒻写博客不易,转载请注明出处,万分感谢!~