2020.4.1 模拟赛游记 & 题解

游记

反正这次比赛不知道怎么的就炸了。。。

( ext{T1}) 一看,弱智题。然后就切了。

( ext{T2}) 一看,一开始感觉是暴力枚举。

但是后来一想是鸡兔同笼 Dev-c++:那你还调试那么多次 然后就切了。

( ext{T3}) 进入之后,感觉自己人生巅峰了。

一共 (6) 题,前 (3) 题都是水题?

来到 ( ext{T4}),发现不太简单。

应该是个大模拟,欺负我没玩过 ( exttt{zuma}) 是吧?(好下次把三国杀、植物大战僵尸都玩了来切题

然后想了想,既然要支持删除,那就 ( exttt{vector}) 上!

结果中间发现会有连环效应。

就用一个变量指着中间插入东西的位置,然后一直在那个位置往两边删除就行了?

当时我对题意有一点点的误解,导致后来就 AC 了(???)

反正当时觉得不太行。

( ext{T5}) 一开始盲猜数学题。

这种拆分是数学题吧?

当时呢,就按照尽量让 (x^k leq n) 且接近的贪心去贪了。

中间的二分因为精度炸了好几次,快速幂也不太行。。

调出来之后自认为对了,然后谔谔。。

来到 ( ext{T6}),发现仍然不是很简单吧。。。

好像之前做过类似的题?

第一感觉:二分。

但是看看数据范围发现不是。

(n^2 log n) 过不了,(n log n) 怎么可能出 (n leq 3000)

然后就弄掉二分了。(赛后才知道,那玩意儿没有单调性,幸亏没用二分啊)

突然发现是个 (O(n^2)) 暴力?然后就切掉了。

这难度似乎不是单调增的吧

( ext{T7}),求最大全 (0) 子矩阵。

随便找一个网上板子就行

结果后来写 ( exttt{dp}) 写了半天才调试出来。

然后去洛谷 AC 掉模板题之后就开始咕了。。。

题解

(T1) ~ (T3) 水题,不管它了。(如果不会那你可以退役了)

(T4) 简要题意:

每次在数组中间插入一个数,只要 当前插入的这个数与左 / 右 形成 (geq 3) 个相同的数,就消去它们。可能引起连环效应,但注意:如果不是与插入的数进行效应则不抵消;求最后的数组。

首先你要支持插入和删除,第一反应是链表。(毕竟链表快啊)

但是链表的细节很繁琐,本人又不太会用指针。 不会就是不会,为什么说不太会呢

然后就想了想,用 ( exttt{vector}) 应该可以。

中途突然发现样例和题目描述有点冲突。

万一我在 (x) 位置消除的时候,若干次后引起 (x+1 , x+2 , x+3) 相等,那么抵消吗?

当时盲猜抵消,感觉自己没法做。

但是 (A) 题了是真的,所以抵消!(谔谔)

可是为什么A题的只有我一个?(还不是因为别人不屑于做题吧)

(调试代码没有去,码风太丑,而且还用了 ( ext{goto}),我码力太烂)

时间复杂度:(O(n + m log n)).(数据太弱了,数组都能过;不过不想写太多细节吧)

实际得分:(100pts).

(下面给出的是我考试时的代码,如果有文字是注释;注释掉的英文是我原来的调试代码)

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}

vector<int>v;
int n,m,c,x;

inline void print(char c) {
	putchar(c); putchar(' ');
	for(int i=0;i<v.size();i++) printf("%d ",v[i]);
	putchar('
'); return;
}

inline void solve() {
	v.insert(v.begin()+x,c); //插入
	int zuo=x,you=x; bool f=0;
	goto fin; //清奇的码风开始了 
	fin: {
/*	while(1) {*/
//		print('a'); printf("h %d %d %d
",x,zuo,you);
		for(int i=x-1;i>=0;i--)
			if(v[i]!=v[i+1]) {zuo=i+1;break;}
			else if(i==0) zuo=i; //往左统计相同
		for(int i=(!f)?(x+1):x;i<v.size();i++) //f 用来表示当前是第一次还是其它;因为第一次 x,x+1,x+2 是抵消的
			if(v[i]!=v[i-1]) {you=i-1;brak;}
			else if(i+1==v.size()) you=i; //往右统计相同
//		printf("b %d %d %d
",x,zuo,you); 
/*	}*/	if(!f) { //第一次
			if(you-zuo+1<3) {
//				printf("c %d %d %d
",x,zuo,you);
//				print('d');
				return;
			} //小于 3 就结束
			v.erase(v.begin()+zuo,v.begin()+you+1); //整个区间删除
			x=zuo; zuo=x; you=x-1; //继续指向位置
		} else{
			if(you-zuo+1<3) {
//				printf("e %d %d %d
",x,zuo,you);
//				print('f');
				return;
			}
			v.erase(v.begin()+zuo,v.begin()+you+1);
			x=zuo; zuo=x; you=x-1;  //代码一模一样,是因为一开始我对题目有无解后来改的;其实可以直接合并
		} /*print('g');*/ f=1; goto fin; //神奇的 goto ,自己跳往自己实现循环,好东西
	}
}

int main(){
	freopen("zuma.in","r",stdin);
	freopen("zuma.out","w",stdout);
	n=read(),m=read();
	for(int i=1;i<=n;i++) v.push_back(read());
	while(m--) {
		c=read(),x=read();
		solve();/*for(int i=0;i<v.size();i++) printf("%d ",v[i]);
	putchar('
');*/
	} /*for(int i=0;i<v.size();i++) printf("%d ",v[i]);
	putchar('
');*/
	printf("%d
",v.size());
	return 0;
}

( ext{T5}),是防 ( exttt{AK}) 的(结果还真防着我了)

显然,用 ( ext{dp}).

但是,如果线性推是不现实的。

我们注意到,(f_{n,k}) 只与 (f_{i,k-1} (i | k)) 有关,枚举因数即可。

这时就要用记忆化搜索(好东西!),其实记忆化搜索比 ( ext{dp}) 灵活一点。

然后不断往下即可。

如果你听过 (Q) 老师讲,现在你懂了;否则你一脸懵逼。

好,用 (f_{n,k}) 表示将 (n) 分解成 (k) 个正整数乘积,最小的和。

那么:

[f_{n,k} = egin{cases} n , k = 1 \ min_{i|n} min(f_{i,k-1} + frac{n}{i} , f_{frac{n}{i},k-1} + i) , k ot = 1 \ end{cases} ]

时间复杂度:(O(sqrt{n})).(实际上我个人不太认同,毕竟要一层层下去的;但是通过实践表明,时间复杂度就大概在这个级别。但是常数比较大的)

实际得分:(100pts).(不好意思,直接复制标程了)

#include <bits/stdc++.h>
using namespace std;

int n, k;
map<int, map<int, int> > f;
int F(int n, int k) {
    if (k == 1) return n; //边界
    if (f[n][k]) return f[n][k]; //记忆化
    int ret = n + k - 1; //分解成 n 和一堆 1
    for (int i = 2; i * i <= n; ++i)
        if (n % i == 0)
            ret = min(ret, min(F(i, k - 1) + n / i, F(n / i, k - 1) + i)); //转移
    f[n][k] = ret;
    return ret;
}
int main() {
    scanf("%d%d", &n, &k);
    printf("%d
", F(n, k));
    return 0;
}

( ext{T6}) 弱智题,直接暴力枚举,暴力验证。

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

实际得分:(100pts).

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

const int N=5e3+1;
#define EPS INT_MAX

inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}

int n,a[N],f[N];
int ans=EPS,ansk=EPS;

int slove(int k) {
	memset(f,0,sizeof(f));
	int sum=0,ans=0;
	for (int i=1;i<=n-k+1;i++) {
		if((a[i]+sum)&1) ans++,f[i]=1; //位运算有点恶心,&1 相当于 %2 (在这里)
		sum=sum+f[i];
		if(i-k+1>=1) sum=sum-f[i-k+1];
	}
	for (int i=n-k+2;i<=n;i++) {
		if((a[i]+sum)&1) return -1;
		if(i-k+1>=1) sum=sum-f[i-k+1];
	} return ans;
}
 
int main() {
	freopen("cow.in","r",stdin);
	freopen("cow.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for (int k=1,t;k<=n;k++){ //暴力枚举
		t=slove(k);
		if (t!=-1 && t<ans) ans=t,ansk=k;
	} printf("%d
",ansk);
	return 0;
}

( ext{T7}) 又是唯一 ( exttt{AC}),真不明白,模板题这么难???

算法一

暴力枚举矩阵左上角,右下角,然后暴力求和。

时间复杂度:(O(n^6)).

实际得分:(0pt).

算法二

用二维前缀和优化算法一.

时间复杂度:(O(n^4)).

实际得分:(0pt).

算法三

对每一行,枚举左右端点(即线段),然后用此线段向上枚举高。

时间复杂度:(O(n^3)).

实际得分:(0pt).(如果你开 ( exttt{O2}) 没准可以卡一卡)

算法四

滚来看看这 洛谷模板题的题解

然后你直接复制过来就行了。(别忘记 (div 3) 啊。。)

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

实际得分:(100pts).

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

const int N=2e3+1;

inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}

int n,m,a[N][N],h[N][N];
int l[N][N],r[N][N],ans=0;

int main(){
	freopen("pool.in","r",stdin);
	freopen("pool.out","w",stdout);
	n=read(),m=read();
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++) a[i][j]=read();
	for(int j=1;j<=m;j++) r[0][j]=m+1; //右边
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++) h[i][j]=(!a[i][j])?(h[i-1][j]+1):0; //向上
		int t=0;
		for(int j=1;j<=m;j++)
			if(!a[i][j]) l[i][j]=max(l[i-1][j],t); //向左
			else l[i][j]=0,t=j;
		t=m+1; for(int j=m;j;j--)
			if(!a[i][j]) r[i][j]=min(r[i-1][j],t);
			else r[i][j]=m+1,t=j; //向右
		for(int j=1;j<=m;j++) ans=max(ans,(r[i][j]-l[i][j]-1)*h[i][j]);	 //统计	
	} printf("%d
",ans);
	return 0;
}

原文地址:https://www.cnblogs.com/bifanwen/p/12612829.html