2021.12.4上课题目内容回顾

完成情况:(7/9)

cf1110e

题目链接

我要是在noip前做这道题就好了。

这道题的本质就是noip2021方差中的一个性质,对于每个数进行修改,就是把它左右的差进行交换。

注意的是首项一定要一样。

Code

// Problem: CF1110E Magic Stones
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF1110E
// Memory Limit: 250 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
//#define M
//#define mo
#define N 200010
int n, m, i, j, k; 
int a[N]; 
map<int, int>mp; 

signed main()
{
//	freopen("tiaoshi.in", "r", stdin); 
//	freopen("tiaoshi.out", "w", stdout); 
	n=read(); 
	for(i=1; i<=n; ++i) a[i]=read(); 
	k=a[1]; 
	for(i=1; i<=n; ++i) mp[a[i]-a[i-1]]++; 
	for(i=1; i<=n; ++i) a[i]=read(); 
	if(a[1]!=k) return printf("No\n"), 0; 
	for(i=1; i<=n; ++i) 
		if(mp[a[i]-a[i-1]]==0) return printf("No\n"), 0; 
		else mp[a[i]-a[i-1]]--; 
	printf("Yes"); 
	return 0; 
}

poj1191

题目链接

题目

将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)

原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差,其中平均值,xi为第i块矩形棋盘的总分。
请编程对给出的棋盘及n,求出O'的最小值。

思路

化简式子可知原式是求:

\[\Large\sqrt{\frac{\sum_{i=1}^n x_i^2-\frac{(\sum_{i=1}^n x_i)^2}{n}}{n}} \]

\(\frac{(\sum_{i=1}^n x_i)^2}{n}\) 可以直接求出来,所以主要是弄前面一部分。

\(dp(lx, ly, rx, ry, k)\) 表示在 \((lx, ly)\)\((rx, ry)\) 的区间里分成 \(k\) 块每块的平方和最小值。

转移时可以考虑横切竖切的情况。

为了方便统计答案,上面的式子 \(\sqrt{\frac{\sum_{i=1}^n x_i^2-\frac{(\sum_{i=1}^n x_i)^2}{n}}{n}}\) 可以化简为:

\[\Large\sqrt{\frac{\sum_{i=1}^n x_i^2}{n}-(\frac{\sum_{i=1}^n x_i}{n})^2} \]

用记忆化搜索实现会更好。

Code

// Problem: 棋盘分割
// Contest: POJ - Noi 99
// URL: http://poj.org/problem?id=1191
// Memory Limit: 10 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// #include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
//#define M
//#define mo
#define N 10
int n, m, i, j, k; 
int f[N][N][N][N][16]; 
int s[N][N], a[N][N], sigam[N][N][N][N]; 
double he; 

int sum(int lx, int ly, int rx, int ry)
{
	if(sigam[lx][ly][rx][ry]!=-1) return sigam[lx][ly][rx][ry]; 
	int Sum=s[rx][ry]-s[lx-1][ry]-s[rx][ly-1]+s[lx-1][ly-1]; 
	return sigam[lx][ly][rx][ry]=Sum*Sum; 
}

int dfs(int lx, int ly, int rx, int ry, int k)
{
	if(f[lx][ly][rx][ry][k]!=-1) return f[lx][ly][rx][ry][k]; 
	if(k==1) return f[lx][ly][rx][ry][k]=sum(lx, ly, rx, ry); 
	int ans=999999999999; 
	for(int i=lx; i<rx; ++i)
	{
		ans=min(ans, dfs(lx, ly, i, ry, k-1)+sum(i+1, ly, rx, ry)); 
		ans=min(ans, sum(lx, ly, i, ry)+dfs(i+1, ly, rx, ry, k-1)); 
	}
	for(int i=ly; i<ry; ++i)
	{
		ans=min(ans, dfs(lx, ly, rx, i, k-1)+sum(lx, i+1, rx, ry)); 
		ans=min(ans, sum(lx, ly, rx, i)+dfs(lx, i+1, rx, ry, k-1)); 
	}
	return f[lx][ly][rx][ry][k]=ans; 
}

signed main()
{
//	freopen("tiaoshi.in", "r", stdin); 
//	freopen("tiaoshi.out", "w", stdout); 
	while(scanf("%lld", &k)!=EOF)
	{
		memset(f, -1, sizeof(f)); 
		memset(sigam, -1, sizeof(sigam)); 
		memset(s, 0, sizeof(s)); 
		n=8; 
		for(i=1; i<=n; ++i)
			for(j=1; j<=n; ++j)
				a[i][j]=read(); 
		for(i=1; i<=n; ++i)
			for(j=1; j<=n; ++j)
				s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j]; 
		he=double(double(s[n][n])/double(k)); 
		// printf("%lld %lld %lf\n", dfs(1, 1, n, n, k), s[n][n], he); 
		printf("%.3f\n", sqrt(double(dfs(1, 1, n, n, k))/double(k)-he*he)); 
	}
	return 0; 
}

hdu7015

题目链接

题目

Define the distance between two strings of the same length as the numbers of the positions where the characters differ in these two strings.

If two strings of the same length has a distance of no more than k, we call these two string satisfy k−matching.

Given a string S of length n and a integer k. For each i∈[1,n−1], split S into substrings A and B, while A=S[1,i] and B=S[i+1,n]. For all the string pairs formed by some non empty substring of A and some non empty substrings of B, count the numbers of pairs satisfying k−matching.

给定一个串 \(S\),对于每一个前缀 \(A\) 以及对应的后缀 \(B\),问有多少个子串对之间的距离不超过 K。
两个相同长度的串的距离定义为他们不同字符的个数。

len <= 3000, K <= 3000

思路

\(dp(i, j)\) 表示从 \(i\)\(j\) 开始的前缀中距离不超过 \(K\) 的最长长度。(假设 \(i<j\))

然后我们考虑对答案的贡献。

然后我们会发现有当分割点不同时会有四种状态:

  • A:\([1, i-1]\) 这个时候没有包括 \(i\),贡献为0
  • B:\([i, i+dp(i,j)-1]\) 这个时候包括了 \(i\),且答案会逐渐递增。
  • C:\([i+dp(i, j), j-1]\) 这个时候 \(i\) 在一边,\(j\) 在一边,答案保持为 \(dp(i, j)\)
  • D:\([j, n]\) 这个时候 \(i\)\(j\) 都在分割点的左边,贡献为0

然后我们尝试对答案数组差分

  • A:\([1, i-1]\) 差分为0
  • B:\([i, i+dp(i,j)-1]\) 差分为1
  • C:\([i+dp(i, j), j-1]\) 差分为0
  • j: 差分为 \(-dp(i, j)\)
  • D:\([j+1, n]\) 这个时候 \(i\)\(j\) 差分为0

然后我们再做一次差分:

  • \(i\)\(1\)
  • \(i+dp(i,j)\)\(-1\)
  • \(j\)\(dp(i,j)\)
  • \(j+1\)\(-dp(i,j)\)

然后我们维护二次差分数组,最后统计答案时做两次前缀和即可。

至于 \(dp(i,j)\) 可以用双指针维护,甚至数组也不用开了。

时间复杂度:\(O(n^2)\)

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
//#define M
//#define mo
#define N 3010
int n, m, i, j, k; 
int c[N], cha[N], ans[N]; 
int f, t, l, len, dif; 
char s[N]; 

signed main()
{
//	freopen("tiaoshi.in", "r", stdin); 
//	freopen("tiaoshi.out", "w", stdout); 
	t=read(); 
	while(t--)
	{
		memset(c, 0, sizeof(c)); 
		n=read(); l=read(); 
		scanf("%s", s+1); 
		for(k=1; k<=n; ++k, dif=len=0)
			for(i=1, j=i+k; j<=n; ++i, ++j)
			{
				while(dif<=l) dif+=(s[i+len]!=s[j+len]), ++len; 
				f=min(len-1, min(k, n-j+1)); 
				c[i]+=1; c[i+f]-=1; c[j]-=f; c[j+1]+=f; 
				len--; dif-=(s[i]!=s[j]); 
			}
		for(i=1; i<=n; ++i) cha[i]=cha[i-1]+c[i]; 
		for(i=1; i<=n; ++i) ans[i]=ans[i-1]+cha[i]; 
		for(i=1; i<n; ++i) printf("%lld\n", ans[i]); 
	}
	return 0; 
}

hdu5550

题目链接

题目

Your company has just constructed a new skyscraper, but you just noticed a terrible problem: there is only space to put one game room on each floor! The game rooms have not been furnished yet, so you can still decide which ones should be for table tennis and which ones should be for pool. There must be at least one game room of each type in the building.

Luckily, you know who will work where in this building (everyone has picked out offices). You know that there will be Ti table tennis players and Pi pool players on each floor. Our goal is to minimize the sum of distances for each employee to their nearest game room. The distance is the difference in floor numbers: 0 if an employee is on the same floor as a game room of their desired type, 1 if the nearest game room of the desired type is exactly one floor above or below the employee, and so on.

计划建设 N 层高的运动楼,每层楼要么建游泳池,要么建乒乓球室,只能二选一。
已知第 i 层有 \(T_i\) 个人打乒乓球,\(P_i\) 个人想游泳。
如果该层楼没有对应的功能室,对应的人只能去最近的楼层运动。
单个人爬一层楼的额外运动量是 1,问最合理的设计下,满足每个人的需求时,最少付出的额外运动量总和是多少。

\(N <= 4000,\ 0 <= T_i,P_i <= 10^{9}\)

思路

\(dp(i, k)\) 表示第 \(i\) 层修建设施 \(k\),且第 \(i+1\) 层修建另一种建筑物,前 \(i\) 层的最小代价。

然后我们枚举一个楼层 \(j\),使 \([j+1, i]\) 层都修建建筑 \(k\),然后我们再让 \([j+1, i]\) 不是 \(k\) 的人去 \(j\)\(i+1\) 层去。

\[\Large dp(i, k)=\min_{j=1}^{i-1}dp(j, k)+go(j+1, i, k') \]

初始化我们假设前 \(i\) 层都修建建筑 \(k\)

注意在转移过程中我们就要统计答案,我们可以假设 \([i+1, n]\) 都修建建筑 \(k'\)

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
//#define M
//#define mo
#define N 4010
int n, m, i, j, k; 
int sum[N][2], dsm[N][2], dp[N][2]; 
int l, r, mid, x, ans, t, T; 

int goup(int l, int r, int k) // [l, r]-> r+1 
{
	return (sum[r][k]-sum[l-1][k])*(r+1)-(dsm[r][k]-dsm[l-1][k]); 
}

int godown(int l, int r, int k) // [l, r]->l-1
{
	return (dsm[r][k]-dsm[l-1][k])-(sum[r][k]-sum[l-1][k])*(l-1); 
}

int go(int l, int r, int k)
{
	mid=(l+r)>>1; 
	return godown(l, mid, k)+goup(mid+1, r, k); 
}

signed main()
{
//	freopen("tiaoshi.in", "r", stdin); 
//	freopen("tiaoshi.out", "w", stdout); 
	T=read(); 
	while(T--)
	{
		n=read(); 
		for(i=0; i<n*2; ++i)
		{
			x=read(); 
			sum[i/2+1][i&1]=sum[i/2][i&1]+x; 
			dsm[i/2+1][i&1]=dsm[i/2][i&1]+x*(i/2+1); 
		}
		ans=0x3f3f3f3f3f3f3f3f; 
		for(i=1; i<n; ++i)
		{
			dp[i][0]=goup(1, i, 1); 
			dp[i][1]=goup(1, i, 0); 
			for(j=1; j<i; ++j)
			{
				dp[i][0]=min(dp[i][0], dp[j][1]+go(j+1, i, 1)); 
				dp[i][1]=min(dp[i][1], dp[j][0]+go(j+1, i, 0)); 
			}
			ans=min(ans, dp[i][0]+godown(i+1, n, 0)); 
			ans=min(ans, dp[i][1]+godown(i+1, n, 1)); 
		}
		printf("Case #%lld: %lld\n", ++t, ans); 
	}
	return 0; 
}

hdu5794

题目链接

题目

There is a n×m board, a chess want to go to the position
(n,m) from the position (1,1).
The chess is able to go to position (x2,y2) from the position (x1,y1), only and if only x1,y1,x2,y2 is satisfied that (x2−x1)2+(y2−y1)2=5, x2>x1, y2>y1.
Unfortunately, there are some obstacles on the board. And the chess never can stay on the grid where has a obstacle.
I want you to tell me, There are how may ways the chess can achieve its goal.

\(N * M\) 的矩阵,从左上角到右下角的方案数。
其中:

  • 相邻两次的步长必须满足:\((y_2 - y_1)^2 + (x_2 - x_1)^2 = 5\ \&\&\ y2 > y1\ \&\&\ x2 > x1\)
  • \(K\) 个格子不能进入。

\(N,M <= 10^{9},\ K<=100\)

思路

我们发现棋盘很大,不可能一个个格子算,于是我们尝试对关键点(不能到的格子和终点)来算。

引理:

\(n\times m\) 的棋盘中,每次可以向下或向右走一格,从左上角到右下角的方案为:

\[\Large C_{n+m}^n=C_{n+m}^m \]

然而这道题中是走日子格,于是我们可以吧棋盘变一下,变成上面的经典问题。

假设当前位置在 \((i, j)\),则从起点要走 \((i+j-2)\) (这是当 \(i\neq 1 ,j\neq 1\) 时,不过数据好像没有卡,所以我代码也没写)个格子到这里,而日子格每次可以走3格,所以会走 \(\frac{(i+j-2)}{3}\) 步。每次走日子格都至少向下和向右一格,然后是向下或向右一格,所以 \((i, j)\) 就可以转化为 \((i-\frac{(i+j-2)}{3}, j-\frac{(i+j-2)}{3})\)。不合法的情况还需要特判一下。

\(dp_i\) 表示到达第 \(i\) 个关键点的合法方案数。然后我们套容斥枚举第一个不合法的点,假设是 \(dp_j\),然后再拿乘上从 \(j\) 走到 \(i\) 的方案数。

\[\Large dp_i=f(1, 1, x_i, y_i)-\sum_{j=1}^{i-1}(dp_j\times f(x_j, y_j, x_i, y_i)) \]

组合数可以用lacus定理来优化。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
//#define M
#define p 110119
#define NN 120
struct node
{
	int x, y; 
}a[NN]; 
int n, m, i, j, k; 
int jc[p*3], dp[NN]; 
int f, ans, N, M, t; 

bool cmp(node x, node y)
{
	if(x.x==y.x) return x.y<y.y; 
	return x.x<y.x; 
}

int kuai(int a, int b)
{
	int ans=1;  
	while(b)
	{
		if(b&1) ans=(ans*a)%p; 
		b>>=1; 
		a=(a*a)%p; 
	}
	return ans; 
}

int C(int m, int n)
{
	if(m<n) return 0; 
	return jc[m]*kuai(jc[n]*jc[m-n]%p, p-2); 
}

int lacus(int m, int n)
{
	if(m==0) return 1; 
	return lacus(m/p, n/p)*C(m%p, n%p)%p; 
}

int check(int a, int b)
{
	if((a+b-2)%3) return 0;  
	// printf("-------\n"); 
	f=(a+b-2)/3; a-=f; b-=f; 
	if(a<=0||b<=0) return 0; 
	return 1; 
}

int count(int lx, int ly, int rx, int ry)
{
 	if(!check(lx, ly)) return 0; 
 	if(!check(rx, ry)) return 0; 
 	f=(lx+ly-2)/3; lx-=f; ly-=f; 
 	f=(rx+ry-2)/3; rx-=f; ry-=f; 
 	if(rx-lx<0) return 0; 
 	if(ry-ly<0) return 0; 
 	return lacus(rx-lx+ry-ly, rx-lx); 
}

signed main()
{
//	freopen("tiaoshi.in", "r", stdin); 
//	freopen("tiaoshi.out", "w", stdout); 
	for(i=jc[0]=1; i<=p; ++i) jc[i]=jc[i-1]*i%p; 
	while(scanf("%lld%lld%lld", &N, &M, &n)!=EOF)
	{
		printf("Case #%lld: ", ++t); 
		for(i=1; i<=n; ++i) a[i].x=read(), a[i].y=read(); 
		sort(a+1, a+n+1, cmp); 
		// printf("%lld %lld\n", N, M); 
		if(!check(N, M)) {printf("0\n"); continue; }
		for(i=1; i<=n; ++i)
		{
			dp[i]=count(1, 1, a[i].x, a[i].y); 
			for(j=1; j<i; ++j)
				dp[i]=((dp[i]-dp[j]*count(a[j].x, a[j].y, a[i].x, a[i].y)%p)%p+p)%p; 
			// printf("dp[%lld]=%lld\n", i, dp[i]); 
		}
		ans=count(1, 1, N, M); 
		for(i=1; i<=n; ++i)
			ans=((ans-dp[i]*count(a[i].x, a[i].y, N, M)%p)%p+p)%p; 
		printf("%lld\n", ans); 
	}
	return 0; 
}

uvalive3363

题目链接

题目

Bubu’s bookshelf is in a mess! Help him!
There are nbooks on his bookshelf. We define the mess value to be the number of segments of
consecutive equal-height books. For example, if the book heights are 30, 30, 31, 31, 32, the mess
value is 3, that of 30, 32, 32, 31 is also 3, but the mess value of 31, 32, 31, 32, 31 is 5 — it’s indeed in
a mess!
Bubu wants to reduce the mess value as much as possible, but he’s a little bit tired, so he decided
to take out at most kbooks, then put them back somewhere in the shelf. Could you help him?

书架上有 N 本书,书本高度是 [25cm,32cm],凌乱地排着。
定义一排书的整齐度是指最少可以被划分成高度连续相等的书的段数
比如 {30,32,32,31} 整齐度是 3。
比如 {31,32,31,32,31} 整齐度是 5。
现在最多从其中拿出 K 本书,然后塞回去。
问整理后,最少可以有多少段高度相等的连续区间。

K <= N <= 100

思路

发现书本高度最大最小差为8,很容易想到状压dp。

\(dp(i, j, k, o)\) 表示前 \(i\) 本书里拿出 \(j\) 本,剩下书高度集合为 \(k\),且最后一本书高度为 \(o\) 的最小凌乱度。

然后枚举每一本书拿或者不拿。

如果不拿,则 \(k\) 变成 \(k|(1<<a_i)\),代表这本书放入未被拿的集合。\(o\) 变成 \(a_i\)。如果之前 \(o\neq a_i\),则要加1。

如果拿,剩下书的集合还有最后一本不变。然后我们考虑是否+1,如果为拿出的集合中有这本书,我们可以把现在这本书插到它旁边,然后不用+1。如果后面还有和它一样的书,我们就把现在这本书的命运交给后面那本书。除了这两种情况,其它都要+1。

可能会MLE,要滚动数组优化。

Code

// Problem: UVA12235 Help Bubu
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/UVA12235
// Memory Limit: 0 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
//#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
//#define M
//#define mo
#define N 110
int n, m, i, j, k; 
int ans, dp[2][N][300][10]; 
int is[1010][10], a[1010]; 
int o, nw, ls, K, t; 

void minn(int &x, int y)
{
	x=min(x, y); 
}

signed main()
{
//	freopen("tiaoshi.in", "r", stdin); 
//	freopen("tiaoshi.out", "w", stdout); 
	scanf("%d%d", &n, &K);
	while(n)
	{
		memset(dp, 0x3f, sizeof(dp)); 
		memset(is, 0, sizeof(is)); 
		for(i=1; i<=n; ++i) a[i]=read()-25; 
		// for(i=1; i<=n; ++i) printf("%d ", a[i]);  
		// printf("\n"); 
		for(i=1; i<=n; ++i)
			for(j=i+1; j<=n; ++j)
				is[i][a[j]]=1; 
		ans=dp[0][0][0][0]; 
		dp[0][0][0][8]=0; 
		for(i=1; i<=n; ++i)
		{
			nw=i&1; ls=(!nw); 
			memset(dp[nw], 0x3f, sizeof(dp[nw])); 
			for(j=0; j<=K; ++j)
				for(k=0; k<=255; ++k)
					for(o=0; o<=8; ++o)
						if(o==8||((1<<o)&k))//之前未选集合中含有最后一个
						{
							minn(dp[nw][j][k|(1<<a[i])][a[i]], dp[ls][j][k][o]+(a[i]!=o)); 
							if(j) minn(dp[nw][j][k][o], dp[ls][j-1][k][o]+( (((1<<a[i])&k)||(is[i][a[i]])) ? 0 : 1 ) ); 
						}
		}
		for(j=0; j<=K; ++j)
			for(k=0; k<=255; ++k)
				for(o=0; o<=8; ++o)
					if(o==8||((1<<o)&k)) 
						ans=min(ans, dp[n&1][j][k][o]); 
		printf("Case %d: %d\n\n", ++t, ans); 
		scanf("%d%d", &n, &K);
	}
	return 0; 
}

posted @ 2021-12-09 19:56  zhangtingxi  阅读(40)  评论(0编辑  收藏  举报