# 8.19考试总结

8.19考试总结

预计得分:230 实际得分:180 丢分:50

真的是哪题不拍挂哪题

T1

方差的式子是

\[\frac{\sum a_i^2}{n} - \frac{(\sum a_i)^2}{n^2} \]

题目中式子其实要求的是

\[n\sum a_i^2-(\sum a_i)^2 \]

想DP发现这两个东西不好同时搞

我们就DP一维记录一维

记录肯定考虑记录值域最小的

我们就设\(f_{i,j,k}\)他去表示

到了\((i,j)\)这个点

\(\sum a_i\)的值为\(k\)的最大的\(\sum a_i^2\)

转移显然

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 35;
const int M = 1835;
int f[N][N][M];
int a[N][N];
int n,m;int num = 0;
inline int read(){
	int v = 0,c = 1;char ch = getchar();
	while(!isdigit(ch)){
		if(ch == '-') c = -1;
		ch = getchar();
	}
	while(isdigit(ch)){
		v = v * 10 + ch - 48;
		ch = getchar();
	}
	return v * c;
}
int main(){
//	freopen("path.in","r",stdin);
	int T = read();
	while(T--){
		memset(f,0x3f,sizeof(f));
		n = read(),m = read();
		for(int i = 1;i <= n;++i)
			for(int j = 1;j <= m;++j) a[i][j] = read();//cout << "GG" << endl;
	//	memset(f,0x3f,sizeof(f));
		f[1][1][a[1][1]] = a[1][1] * a[1][1];
		for(int i = 1;i <= n;++i){
			for(int j = 1;j <= m;++j){
				for(int k = 0;k <= (i + j - 1) * 30;++k){
					if(k + a[i + 1][j] <= (i + j) * 30) f[i + 1][j][k + a[i + 1][j]] = min(f[i + 1][j][k + a[i + 1][j]],
					f[i][j][k] + a[i + 1][j] * a[i + 1][j]);
					if(k + a[i][j + 1] <= (i + j) * 30) f[i][j + 1][k + a[i][j + 1]] = min(f[i][j + 1][k + a[i][j + 1]],
					f[i][j][k] + a[i][j + 1] * a[i][j + 1]);  
//					if(k + a[i + 1][j] > (i + j) * 30)		
				}
			}
		}
		LL ans = 0x3f3f3f3f;
		for(int i = 0;i <= 1800;++i)  ans = min(ans,1ll * (n + m - 1) * f[n][m][i] - i * i);
		 
		printf("Case #%d: %lld\n",++num,ans);
	}
	return 0;
}

T2

挂烂了QAQ

考虑优雅的暴力建边

mGVXpF.png

这是暴力建边方式时间复杂度\(O(nm)\)

正确性显然

发现每次都是一个点向一个区间连边,直接线段树优化这个过程

而由于边权只有\(1/0\)

最短路径可以\(01\)bfs

关于线段树优化建图的本质,这里就简单的说一下

本质是维护了两颗线段树,一颗表示入边另外一颗表示出边

区间操作就可以通过虚点来进行连接

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#include<deque>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 1e6 + 3;
int pos[N];
int n,m,s;
int whe;
struct tree{
	int head[N << 2];
	int dis[N << 2];
	int rt1,rt2,tot,t;
	struct node{
		int lc;
		int rc;
	}a[N << 2];
	struct edge{
		int to;
		int nxt;
		int data;
	}e[N * 35];
	inline void add(int x,int y,int z){
		e[++tot].to = y;
		e[tot].data = z;
		e[tot].nxt = head[x];
		head[x] = tot;
	}
	inline void build(int &u,int l,int r,bool flag){
		if(!u) u = ++t;
		if(l == r) {if(!flag) pos[l] = u;return;}
		int mid = (l + r) >> 1;
		build(a[u].lc,l,mid,flag);build(a[u].rc,mid + 1,r,flag);
		if(flag){
			add(a[u].lc,u,0);
			add(a[u].rc,u,0);	
		}
		else{
			add(u,a[u].lc,0);
			add(u,a[u].rc,0);	
		}
	}
	inline void updata(int u,int l,int r,int ll,int rr,int p,bool flag){
		if(l == ll && r == rr){
			if(flag) add(u,p,0);
			else add(p,u,0);
			return ;
		}
		int mid = (l + r) >> 1;
		if(rr <= mid) updata(a[u].lc,l,mid,ll,rr,p,flag);
		else if(ll > mid) updata(a[u].rc,mid + 1,r,ll,rr,p,flag);
		else{
			updata(a[u].lc,l,mid,ll,mid,p,flag);
			updata(a[u].rc,mid + 1,r,mid + 1,rr,p,flag);
		}
	}
	inline void ADD(int r1,int r2,int l,int r){
		if(l == r){
			add(r2,r1,0);
			return ; 
		}
		int mid = (l + r) >> 1;
		ADD(a[r1].lc,a[r2].lc,l,mid);ADD(a[r1].rc,a[r2].rc,mid + 1,r);
	}
	inline void link(int l,int r,int ll,int rr){
		updata(rt1,1,n,l,r,++t,1);
		updata(rt2,1,n,ll,rr,++t,0);
		add(t - 1,t,1);	
	}
	inline void bfs(int x){
		memset(dis,0x3f,sizeof(dis));
		dis[x] = 0;
		deque <int> q;
		q.push_back(x);
		while(!q.empty()){
			int k = q.front();
			q.pop_front();
			for(int i = head[k];i;i = e[i].nxt){
				int y = e[i].to;		
				if(dis[y] > dis[k] + e[i].data){
					dis[y] = dis[k] + e[i].data;
					if(e[i].data) q.push_back(y);
					else q.push_front(y);	
				}
			}
		}
		for(int i = 1;i <= n;++i) printf("%d\n",dis[pos[i]]);
	}
}T;
inline int read(){
	int v = 0,c = 1;char ch = getchar();
	while(!isdigit(ch)){
		if(ch == '-') c = -1;
		ch = getchar();
	}
	while(isdigit(ch)){
		v = v * 10 + ch - 48;
		ch = getchar();
	}
	return v * c;
}
int main(){
	n = read(),m = read(),s = read();
	T.build(T.rt1,1,n,1);
	T.build(T.rt2,1,n,0);
	T.ADD(T.rt1,T.rt2,1,n);
	for(int i = 1;i <= m;++i){
		int l = read(),r = read(),ll = read(),rr = read();
		T.link(l,r,ll,rr);
		T.link(ll,rr,l,r);
	}
//	printf("%d\n",pos[s]);
	T.bfs(pos[s]);
	return 0;
}

T3

\(n = 1000\)被我\(n^3\)卡过去了可还行

我到现在还是怀疑题解的思路是错的,

说一下另外一个\(n^2\)做法

把矩阵查分两次之后

就变成了求最大的全\(0\)子矩阵(可能细节和边界要特殊处理)

这个直接悬线法求就好了

posted @ 2019-08-20 11:15  wyxdrqcccc  阅读(153)  评论(0编辑  收藏  举报