1. 程式人生 > 其它 >NOIP模擬38:a

NOIP模擬38:a

  這是T1。
  考場上思路與正解就差個字首,打的線段樹,因為其巨大常數快樂掛掉。。。。。。
  正解複雜度是\(O(n^2m)\),其實再掛個\(log\)也能過,但是需要用常數極其優秀的樹狀陣列外加大大大大大大大大大大大大大大大大力卡常。
  有點像之前做的入陣曲一題。
  首先\(n\)很小,那麼考慮\(O(n^2)\)列舉上下界,然後用一個指標掃一邊,具體實現比較像入陣曲,可以參考著看。
  大部分思路是一樣的,只是有一點,這題要做桶的字首和。
  首先要證明一點就是點個數比自己小的矩陣一定在自己前面,那就可以統計完桶後,直接字首,字首可以直接用。
  其實即使沒有這個性質也可以直接統計桶,做字首然後加減,因為即使桶所記錄的矩陣在自己後面,從自己這裡統計也沒關係,只要能保證一一對應不會統計重或漏即可


  這也可以看作統計的一個原則,就是隻要不重不漏,統計方式是無所謂的。

Code
#include<bits/stdc++.h>
using namespace std;
namespace STD
{
	#define rr register
	#define scanf ybbb=scanf
	typedef long long ll;
	const int M=5e4+4;
	const int N=32;
	int n,m,l,r,ybbb;
	char s[M];
	int mat[N][M];
	int ton[30*M];
	int read()
	{
		rr int x_read=0,y_read=1;
		rr char c_read=getchar();
		while(c_read<'0'||c_read>'9')
		{
			if(c_read=='-') y_read=-1;
			c_read=getchar();
		}
		while(c_read<='9'&&c_read>='0')
		{
			x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
			c_read=getchar();
		}
		return x_read*y_read;
	}
};
using namespace STD;
int main()
{
	n=read(),m=read();
	for(rr int i=1;i<=n;i++)
	{	
		scanf("%s",s+1);
		for(rr int j=1;j<=m;j++)
			if(s[j]=='1')
				mat[i][j]=1;
	}
	l=read(),r=read();
	for(rr int i=1;i<=n;i++)
		for(rr int j=1;j<=m;j++)
			mat[i][j]+=mat[i][j-1];
	for(rr int i=1;i<=n;i++)
		for(rr int j=1;j<=m;j++)
			mat[i][j]+=mat[i-1][j];
	ll ans=0;
	for(rr int i=1;i<=n;i++)
		for(rr int j=i;j<=n;j++)
		{
			for(rr int k=1;k<=m;k++)
			{
				if(!l)
					ans+=ton[mat[j][k]-mat[i-1][k]];
				ton[mat[j][k]-mat[i-1][k]]++;
			}
			for(rr int k=1;k<=mat[j][m]-mat[i-1][m];k++)
				ton[k]+=ton[k-1];
			for(rr int k=1;k<=m;k++)
			{
				int temp=mat[j][k]-mat[i-1][k];
				if(l<=temp&&temp<=r) ans++;
				if(l<=temp)
				{
					if(l)
					{
						if(temp>r)
							ans+=ton[temp-l]-ton[temp-r-1];
						else 
							ans+=ton[temp-l];
					}
					else
					{
						if(temp>r)
							ans+=ton[temp-l-1]-ton[temp-r-1];
						else 
							ans+=ton[temp-l-1];
					}
				}
			}
			for(rr int k=0;k<=mat[j][m]-mat[i-1][m];k++)
				ton[k]=0;
		}
	printf("%lld\n",ans);
}