1. 程式人生 > >【NOIP2018模擬賽2018.10.24 B組】

【NOIP2018模擬賽2018.10.24 B組】

此題直接暴力列舉 1~1e6 + 1000 左右預處理一個好數陣列,然後二分找到詢問n位置,往後找m個就行了。。

被行末空格噁心到,,程式碼變醜了好多。。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt putchar
#define ex pt('\n')
#define ko pt(' ')
const int MAXN = 1e6 + 5;
const int INF = 1e6 + 1000;
int T,good[MAXN];
int cnt = 0;
void in(int &x)
{
	int num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9')
		{num = (num << 3) + (num << 1) + (ch - '0'); ch = getchar();}
	x = num*f;
}
void out(int x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x % 10 + '0');
}

bool calc(int x)
{
	int one = 0,two = 0;
	while(x)
	{
		int tmp =  x % 3;
		if(tmp == 1) one++;
		else if(tmp == 2) two++;
		x /= 3;
	}
	return (one == two);
}

void init()
{
	for(int i = 1;i <= INF;i++)
		if(calc(i)) good[++cnt] = i;
}

int main()
{
	init();
	in(T);
	while(T--)
	{
		int n,m;
		in(n); in(m);
		int pos = lower_bound(good+1,good+1+cnt,n) - good;
		for(int i = pos;i < pos+m;i++) 
		{
			out(good[i]);if(i != pos+m-1) ko;
		}
		if(T != 0) ex;
	}
	return 0;
}

此題開始lz寫了個set<string>判重,答案就是set長度,70分,後面字元長了MLE了

正解就是用雜湊判重。

但是這道題用一個雜湊很可能(反正lz沒成功)發生衝突,於是我們可以用黑科技"雙重雜湊“”

就是同時使用兩套雜湊系統,分別用不同的進位制和取模數,只有兩套雜湊對應的一個字串的雜湊值都一樣時我們才認同字串重複。

還是可以用set,但是這次存的是pair<haxi1,haxi2>,這樣就不會爆空間了,答案為set長度。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt putchar
#define ex pt('\n')
#define ko pt(' ')
const int MAXN = 2e5 + 5;
const int MOD1 = 1e9 + 7,MOD2 = 233333333;
const int INF = 1e9;
int n,m;
//string s;
char s[MAXN];
ll f1[MAXN],f2[MAXN],c[MAXN],s1,s2;
set<pair<ll,ll> > so;
int e1 = 31,e2 = 131;
void in(int &x)
{
	int num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9')
		{num = (num << 3) + (num << 1) + (ch - '0'); ch = getchar();}
	x = num*f;
}
void out(int x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x % 10 + '0');
}
int main()
{
	in(n); in(m); 
	f1[0] = f2[0] = 1;
	scanf("%s",s+1);
	for(int i = 1;i <= n;i++) c[i] = s[i] - 'a';
	for(int i = 1;i <= m;i++)	
		f1[i] = f1[i-1]*e1 % MOD1,
		f2[i] = f2[i-1]*e2 % MOD2;
	s1 = s2 = 0;
	for(int i = 1;i <= m;i++)
	{
		s1 = (c[i] + s1*e1 % MOD1) % MOD1;
		s2 = (c[i] + s2*e2 % MOD2) % MOD2;
	}
	so.insert(make_pair(s1,s2));
	for(int i = m+1;i <= n;i++)
	{
		s1 = (s1 - f1[m-1]*c[i-m] % MOD1 + MOD1) % MOD1;
		s2 = (s2 - f2[m-1]*c[i-m] % MOD2 + MOD2) % MOD2;
		s1 = (s1 * e1 + c[i]) % MOD1;
		s2 = (s2 * e2 + c[i]) % MOD2;
		so.insert(make_pair(s1,s2));
	}
	out(so.size());
	return 0;
}

對於此題,我們選擇從方差的表示式入手。

已知k是經過的點的數目,而到達終點經過了 k =  (n+m-1)個點。

令N=(n+m-1),此時x(即海拔)的平均值為(x1+x2+x3+…+xN)/N,(為了方便,用x_來代替x的平均值)。

σ^2=((x1-x_)^2+(x2-x_)^2+…+(xN-x_)^2)/N, 

N^2*σ^2 
=N*((x1-x_)^2+…+(xN-x_)^2) 
=N*((x1^2-2*x1*x_)^2+…+(xN^2-2*xN*x_+x_^2)) 
=N*((x1^2+…+xN^2)-2*x_*(x1+…+xN)+N*(x_^2)) 
然後再將x_=(x1+…+xN)/N帶入,即可得到ans表示式。 
帶入得到答案 ans = N*(x1^2+…+xN^2)-(x1+…+xN)^2。 

那麼設計dp :f[i][j][sum] 代表當前座標在 i,j ,走的總路程為sum時的 x1^2+x2^2+……xn^2 的最小值

最後答案即為max(f[n][m][i]*N - i * i) (0 <= i <= (n+m)*max{h})

程式碼如下:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt putchar
#define ex pt('\n')
#define ko pt(' ')
const int MAXN = 50 + 5;
const int INF = 1e9;
int n,m,k;
int ans = INF;
int h[MAXN][MAXN],pre[MAXN][MAXN],len,maxh = 0;
int f[MAXN][MAXN][5005];
void in(int &x)
{
	int num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9')
		{num = (num << 3) + (num << 1) + (ch - '0'); ch = getchar();}
	x = num*f;
}
void out(int x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x % 10 + '0');
}
 
int times(int x) {return x*x;}
int main()
{
	in(n); in(m); k = n + m - 1;
	for(int i = 1;i <= n;i++)
		for(int j = 1;j <= m;j++)
		{
			in(h[i][j]),maxh = max(maxh,h[i][j]);
			pre[i][j] = times(h[i][j]);
		}
	memset(f,60,sizeof f);
	f[1][1][h[1][1]] = pre[1][1];
	int limit = maxh*k;
	for(int i = 1;i <= n;i++)
		for(int j = 1;j <= m;j++)
			for(int sum = 0;sum <= limit;sum++)
			{
				if(i < n && sum + h[i+1][j] <= limit) 
					f[i+1][j][sum+h[i+1][j]] = min(f[i+1][j][sum+h[i+1][j]],f[i][j][sum] + pre[i+1][j]);
				if(j < m && sum + h[i][j+1] <= limit) 
					f[i][j+1][sum+h[i][j+1]] = min(f[i][j+1][sum+h[i][j+1]],f[i][j][sum] + pre[i][j+1]);
			}
	for(int i = 0;i <= limit;i++)
		if(f[n][m][i] < INF) ans = min(ans,k*f[n][m][i] - i*i);
	out(ans);
	return 0;
}