[关闭]
@xiaoziyao 2021-03-28T10:43:24.000000Z 字数 1910 阅读 759

P4184 [USACO18JAN]Sprinklers P解题报告

解题报告


P4184 [USACO18JAN]Sprinklers P解题报告:

更好的阅读体验

题意

有一个的矩阵,有个关键点落在格点上(保证每行与每列有且仅有一个),求有多少个非空子矩阵满足左上方有一个关键点,右下方也有一个关键点(两个关键点均可以落在矩阵的边缘)。(

分析

题解里差分写法看不懂,来一发比较套路的dp优化吧。

首先不难发现能选用的格点会形成一个类似这样的图形:

  1. .xxx.
  2. .xxx.
  3. .xxx.
  4. xxxx.
  5. xxxx.

每一行能选用的格点是连续的,且左端点,右端点随着行数的增加而不断左移。

那么就很容易预处理:(代表第行区间的左端点的列号),(代表第行区间的右端点的列号),(表示第列能选用的最上方格点的行号)

然后就可以列出式子(为矩阵的右下角,为矩阵的左上角):

那么剩下的工作就很显然了,这个式子可以优化到,有经验的选手可以跳过下面的部分。

,那么就可以继续化:

利用关键点的坐标为的整数,很容易做到

代码

注意题目中关键点的坐标是从开始的。

  1. #include<stdio.h>
  2. const int maxn=100005,mod=1000000007;
  3. int n,pos,ans;
  4. int l[maxn],r[maxn],up[maxn],y[maxn],sum1[maxn],sum2[maxn];
  5. inline int min(int a,int b){
  6. return a<b? a:b;
  7. }
  8. inline int max(int a,int b){
  9. return a>b? a:b;
  10. }
  11. int main(){
  12. scanf("%d",&n);
  13. for(int i=1;i<=n;i++){
  14. int a,b;
  15. scanf("%d%d",&a,&b);
  16. a++,b++,y[a]=b;
  17. }
  18. l[0]=n;
  19. for(int i=1;i<=n;i++)
  20. l[i]=min(l[i-1],y[i]);
  21. r[n+1]=0;
  22. for(int i=n;i>=1;i--)
  23. r[i]=max(r[i+1],y[i]);
  24. pos=r[1];
  25. for(int i=1;i<=n;i++)
  26. while(pos>=1&&pos>=l[i])
  27. up[pos]=i,pos--;
  28. for(int i=1;i<=n;i++)
  29. sum1[i]=(sum1[i-1]+up[i])%mod;
  30. for(int i=1;i<=n;i++)
  31. sum2[i]=(sum2[i-1]+1ll*i*up[i]%mod)%mod;
  32. for(int i=1;i<=n;i++)
  33. ans=(ans+1ll*i*(1ll*(r[i]-l[i])*(r[i]-l[i]+1)/2ll%mod)%mod-1ll*(sum1[r[i]-1]-sum1[l[i]-1]+mod)%mod*r[i]%mod+(sum2[r[i]-1]-sum2[l[i]-1]+mod)%mod)%mod;
  34. printf("%d\n",(ans+mod)%mod);
  35. return 0;
  36. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注