zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
  • 相关阅读:
    时间选择器组件之table避坑指南
    ⼯⼚模式
    外观模式
    中介者模式+装饰器模式
    代理模式
    策略模式
    设计模式--------单例模式
    设计模式--------订阅/发布模式 (观察者)
    对ts的研究
    对react的研究20200724
  • 原文地址:https://www.cnblogs.com/bifanwen/p/12612829.html
Copyright © 2011-2022 走看看