国庆第一天,写作业累了,就打一个 div.3 玩玩。
进度:6/6
有一个若干层的公寓,公寓的 \(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;
}
给定 \(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;
}
初始给你一个元素 \(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;
}
给定一个序列,求最少插入多少数,才能使此序列任意子段和均不为 \(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;
}
Alice
和Bob
进行剪刀石头布的游戏,总共进行 \(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\) 搭配,答案为
最少赢得次数稍微麻烦一点,因为 \(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;
}
给定一个含有 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
的个数为:
拆开括号中两项分别求和:
左式使用二项式定理,右式显然 \(i=0\) 时为 \(0\) :
拆组合数,约掉 \(i!\) 中的一个 \(i\):
提出一个 \(y\),再凑组合数:
变成一般的二项式定理形式:
最终答案为:
预处理出 \(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;
}
原文:https://www.cnblogs.com/ZigZagKmp/p/13758252.html