ZigZagKmp
Think twice, code once.

国庆第一天,写作业累了,就打一个 div.3 玩玩。

进度:6/6

CF1426A

题意简述

有一个若干层的公寓,公寓的 \(1\) 楼有 \(2\) 个房间,其余楼层每一层有 \(x\) 个房间。房间编号从下往上依次编号,如 \(2\) 楼的房间编号为 \(3,4,\cdots,x+2\)\(2\) 楼的房间编号为 \(x+3,x+4,\cdots,2\cdot x+2\)

输入 \(n,x\) ,求出编号为 \(n\) 的房间在第几层。

多测,\(1\le t\le 1000,1\le n,x\le 1000\)

算法标签

模拟 数学

算法分析

方法1:发现 \(n,x\) 不是很大,因此直接枚举楼层,求出对应编号区间,判断 \(n\) 是否在区间内即可。时间复杂度最坏为 \(O(tn)\)

方法2:特判掉 \(n\le 2\) 的数据,根据题意不难推出 \(ans=\lfloor\frac{n-3}{x}\rfloor+2\)

代码实现

#include<bits/stdc++.h>
using namespace std;
template <typename Tp> void read(Tp &x){
	int fh=1;char c=getchar();x=0;
	while(c>'9'||c<'0'){if(c=='-'){fh=-1;}c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c&15);c=getchar();}x*=fh;
}
int n,x;
signed main(){
	int T;
	read(T);
	while(T--){
		read(n);read(x);
		if(n<=2)puts("1");
		else printf("%d\n",(n-3)/x+2);
	}
	return 0;
}

CF1426B

题意简述

给定 \(n\)\(2\times2\) 的矩阵,每种矩阵无限多,问是否能拼成 \(m\times m\) 的对称矩阵。

称一个矩阵 \(a\) 是对称的,当且仅当 \(\forall 1\le i,j\le |a|,a_{i,j}=a_{j,i}\)

多测,\(1\le t\le 100,1\le n,m\le 100\)

算法标签

模拟 数学分析

算法分析

我们证明能拼成 \(m\times m\) 的对称矩阵当且仅当 \(m\) 为偶数且存在一个 \(2\times 2\) 的矩阵为对称矩阵。

如果 \(m\) 为奇数,则显然不能用 \(2\times 2\) 的矩阵拼出 \(m\times m\) 的矩阵,因此 \(m\) 为偶数。

如果不存在 \(2\times 2\) 的对称矩阵,则在左上角的矩阵一定不对称,即 \(a_{1,2}\neq a_{2,1}\) ,显然不是对称矩阵,因此存在一个 \(2\times 2\) 的矩阵为对称矩阵。

反过来,如果 \(m\) 为偶数且存在一个 \(2\times 2\) 的矩阵为对称矩阵,那么我们可以全部使用这个 \(2\times 2\) 的对称矩阵来拼成 \(m\times m\) 的矩阵。不难发现这个矩阵一定对称。

证毕。

因此只需判断 \(m\) 的奇偶性以及 \(n\) 个输入的矩阵是否存在对称矩阵即可。时间复杂度 \(O(tn)\)

代码实现

#include<bits/stdc++.h>
using namespace std;
template <typename Tp> void read(Tp &x){
	int fh=1;char c=getchar();x=0;
	while(c>'9'||c<'0'){if(c=='-'){fh=-1;}c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c&15);c=getchar();}x*=fh;
}
int n,m;
signed main(){
	int T;
	read(T);
	while(T--){
		read(n);read(m);
		int fl=0;
		for(int i=1,a,b,c,d;i<=n;++i){
			read(a);read(b);read(c);read(d);
			if(b==c)fl=1;
		}
		if(m%2==0&&fl)puts("YES");
		else puts("NO");
	}
	return 0;
}

CF1426C

题意简述

初始给你一个元素 \(1\) ,每次操作你可以选择让一个元素 \(+1\) 或者复制一个元素。

求最少需要多少次操作使其总和不小于 \(n\)

多测,\(t\le 1000,1\le n\le 10^9\)

算法标签

贪心 数学

算法分析

为了使答案尽可能大,我们不能浪费步数去给一个不用来复制的数 \(+1\)\(n\)特别小除外)。

因此操作过程必然是把 \(1\) 一直加到 \(x\) ,再把 \(x\) 复制若干多次,直到总和不小于 \(n\)

这时答案为 \((x-1)+\lceil\frac{n}{x}\rceil-1\)

由基本不等式

\((x-1)+\lceil\frac{n}{x}\rceil-1=x+\lceil\frac{n}{x}\rceil-2\ge 2\sqrt n-2\)

当且仅当 \(x=\sqrt n\)取等。

\(\sqrt n\) 不是整数,则由勾函数性质得最小值为 \(\sqrt n\) 两侧最近整点函数值得较小值。

时间复杂度 \(O(t)\)

代码实现

#include<bits/stdc++.h>
using namespace std;
template <typename Tp> void read(Tp &x){
	int fh=1;char c=getchar();x=0;
	while(c>'9'||c<'0'){if(c=='-'){fh=-1;}c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c&15);c=getchar();}x*=fh;
}
int n,m;
int calc(int x){
	return x+(int)ceil(n*1.0/x)-2;
}
signed main(){
	int T;
	read(T);
	while(T--){
		read(n);m=sqrt(n);
		printf("%d\n",min(calc(m),calc(m+1)));
	}
	return 0;
}

CF1426D

题意简述

给定一个序列,求最少插入多少数,才能使此序列任意子段和均不为 \(0\)

插入的数字为任意整数,甚至可以很大或很小。

\(2\le n\le 2\cdot10^5,-10^9\le a_i\le 10^9,a_i\neq 0\)

算法标签

前缀和 hash

算法分析

区间 \([l,r]\) 子段和为 \(0\) 等价于 \(s_r=s_{l-1}\),其中 \(s_i\) 为前缀和。

因此我们只需要在出现前缀和相等时(不妨设为 \(s_i=s_j,i<j\))在 \(j\) 前插入一个很大的数,那么在 \(i-1\) 之前的所有值都不会对后面的前缀和产生影响。

查询前缀和相等需要用 hash ,可以使用 set 等实现。

时间复杂度 \(O(n\log n)\)

代码实现

#include<bits/stdc++.h>
using namespace std;
#define maxn 1000005
#define int long long
template <typename Tp> void read(Tp &x){
	int fh=1;char c=getchar();x=0;
	while(c>'9'||c<'0'){if(c=='-'){fh=-1;}c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c&15);c=getchar();}x*=fh;
}
int n,m;
set<int>st;
int s[maxn];
int sum;
signed main(){
	read(n);
	for(int i=1;i<=n;++i)read(s[i]),s[i]+=s[i-1];
	st.insert(0);
	for(int i=1;i<=n;++i){
		if(st.find(s[i])!=st.end()){
			++sum;st.clear();st.insert(s[i-1]);
		}
		st.insert(s[i]);
	}
	printf("%lld\n",sum);
	return 0;
}

CF1426E

题意简述

AliceBob进行剪刀石头布的游戏,总共进行 \(n\) 局。

Alice出石头 \(a_1\) ​次,出剪刀 \(a_2\) 次,出布 \(a_3\) 次。

Bob出石头 \(b_1\) 次,出剪刀 \(b_2\) ​次,出布 \(b_3\) ​次。

Alice最少赢多少次,最多赢多少次。

\(1\le n\le 10^9\)

算法标签

贪心 模拟

算法分析

最多赢多少次是比较简单的,尽可能让 \(a_1\)\(b_2\) 搭配, \(a_2\)\(b_3\) 搭配, \(a_3\)\(b_1\) 搭配,答案为

\[\min\{a_1,b_2\}+\min\{a_2,b_3\}+\min\{a_3,b_1\} \]

最少赢得次数稍微麻烦一点,因为 \(a_1\) 可能与 \(b_1,b_3\) 搭配, \(a_2\) 可能与 \(b_1,b_2\) 搭配, \(a_3\) 可能与 \(b_2,b_3\) 搭配,答案并不确定。

因此我们考虑按照一定的顺序尽可能进行操作,最后取最小的答案。

时间复杂度 \(O(6!)\)

当然本题也可以转化为线性规划问题用单纯形/网络流求解

代码实现

#include<bits/stdc++.h>
using namespace std;
#define maxn 1000005
#define maxm 2000005
#define inf 0x3f3f3f3f
#define LL long long
#define mod 1000000007
#define local
void file(string s){freopen((s+".in").c_str(),"r",stdin);freopen((s+".out").c_str(),"w",stdout);}
template <typename Tp> void read(Tp &x){
	int fh=1;char c=getchar();x=0;
	while(c>'9'||c<'0'){if(c=='-'){fh=-1;}c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c&15);c=getchar();}x*=fh;
}
int n,m;
int a[4],b[4],c[4],d[4];
int ans1,ans2;
pair<int,int>tmp[10];
signed main(){
	read(n);
	read(a[1]),read(a[2]),read(a[3]);
	read(b[1]),read(b[2]),read(b[3]);
	ans2=min(a[1],b[2])+min(a[2],b[3])+min(a[3],b[1]);
	ans1=inf;
	tmp[1]=make_pair(1,1);
	tmp[2]=make_pair(1,3);
	tmp[3]=make_pair(2,1);
	tmp[4]=make_pair(2,2);
	tmp[5]=make_pair(3,2);
	tmp[6]=make_pair(3,3);
	do{
		int sm=0;
		c[1]=a[1];c[2]=a[2];c[3]=a[3];
		d[1]=b[1];d[2]=b[2];d[3]=b[3];
		for(int i=1;i<=6;++i){
			int tt=min(c[tmp[i].first],d[tmp[i].second]);
			c[tmp[i].first]-=tt;d[tmp[i].second]-=tt;
		}
		sm=min(c[1],d[2])+min(c[2],d[3])+min(c[3],d[1]);
		ans1=min(ans1,sm);
	}while(next_permutation(tmp+1,tmp+7));
	printf("%d %d\n",ans1,ans2);
	return 0;
}

CF1426F

题意简述

给定一个含有 abc? 的字符串,? 可能是 abc 中的任意一个,求所有可能的无 ? 字符串中子序列 abc 出现的次数。

\(3\le |s|\le 2\cdot10^5\)

算法标签

组合计数

算法分析

因为是 abc 的子序列,所以我们考虑在所有可能的 b 处统计答案,对于当前的 \(b\), 答案为其左侧所有可能的 a 的个数和其左侧所有可能的 c 的个数。

下面只讨论左边的 a 的可能个数,右边的 c 同理。

假设左边有 \(x\)a\(y\)?

考虑有 \(i\)? 变成了 a ,则剩余的 \(y-i\)? 都可以是 b/c\(2^{y-i}\) 种可能,则所有可能的 a 的个数为:

\[\sum_{i=0}^{y}\binom{y}{i}2^{y-i}\cdot\left(x+i\right) \]

拆开括号中两项分别求和:

\[\sum_{i=0}^{y}\binom{y}{i}2^{y-i}x+\sum_{i=0}^{y}\binom{y}{i}2^{y-i}i \]

左式使用二项式定理,右式显然 \(i=0\) 时为 \(0\)

\[x\cdot3^y+\sum_{i=1}^{y}\binom{y}{i}2^{y-i}i \]

拆组合数,约掉 \(i!\) 中的一个 \(i\)

\[x\cdot3^y+\sum_{i=1}^{y}\frac{y!}{(i-1)!(y-i)!}{i}2^{y-i} \]

提出一个 \(y\),再凑组合数:

\[x\cdot3^y+y\sum_{i=1}^{y}\binom{y-1}{y-i}2^{y-i} \]

变成一般的二项式定理形式:

\[x\cdot3^y+y\sum_{i=0}^{y-1}\binom{y-1}{i}2^{i} \]

最终答案为:

\[x\cdot3^y+y\cdot3^{y-1} \]

预处理出 \(3^x\) 即可 \(O(n)\) 求解。

代码实现

#include<bits/stdc++.h>
using namespace std;
#define maxn 1000005
#define maxm 2000005
#define inf 0x3f3f3f3f
#define int long long
#define mod 1000000007
void file(string s){freopen((s+".in").c_str(),"r",stdin);freopen((s+".out").c_str(),"w",stdout);}
template <typename Tp> void read(Tp &x){
	int fh=1;char c=getchar();x=0;
	while(c>'9'||c<'0'){if(c=='-'){fh=-1;}c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c&15);c=getchar();}x*=fh;
}
int n,m;
int p3[maxn];
char st[maxn];
int sa,sc,bw,sw;
int ans;
signed main(){
	read(n);
	p3[0]=1;
	for(int i=1;i<=n;++i)p3[i]=1ll*p3[i-1]*3%mod;
	scanf("%s",st+1);
	for(int i=1;i<=n;++i){
		if(st[i]=='c')sc++;
		if(st[i]=='?')bw++;
	}
	for(int i=1;i<=n;++i){
		if(st[i]=='c')sc--;
		if(st[i]=='?')bw--;
		if(st[i]=='?'||st[i]=='b'){
			int sum1=0,sum2=0;
			sum1=(sum1+1ll*sa*p3[sw]%mod)%mod;
			if(sw)sum1=(sum1+1ll*sw*p3[sw-1]%mod)%mod;
			sum2=(sum2+1ll*sc*p3[bw]%mod)%mod;
			if(bw)sum2=(sum2+1ll*bw*p3[bw-1]%mod)%mod;
			ans=(ans+1ll*sum1*sum2%mod)%mod;
		}
		if(st[i]=='a')sa++;
		if(st[i]=='?')sw++;
	}
	printf("%lld\n",ans);
	return 0;
}
posted on 2020-10-01 18:09  ZigZagKmp  阅读(333)  评论(0编辑  收藏  举报