zoukankan      html  css  js  c++  java
  • Codeforces 1426(#674 Div3.)题解

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

    进度:6/6

    CF1426A

    题意简述

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

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

    多测,(1le tle 1000,1le n,xle 1000)

    算法标签

    模拟 数学

    算法分析

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

    方法2:特判掉 (nle 2) 的数据,根据题意不难推出 (ans=lfloorfrac{n-3}{x} floor+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-3)/x+2);
    	}
    	return 0;
    }
    

    CF1426B

    题意简述

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

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

    多测,(1le tle 100,1le n,mle 100)

    算法标签

    模拟 数学分析

    算法分析

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

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

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

    反过来,如果 (m) 为偶数且存在一个 (2 imes 2) 的矩阵为对称矩阵,那么我们可以全部使用这个 (2 imes 2) 的对称矩阵来拼成 (m imes 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)

    多测,(tle 1000,1le nle 10^9)

    算法标签

    贪心 数学

    算法分析

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

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

    这时答案为 ((x-1)+lceilfrac{n}{x} ceil-1)

    由基本不等式

    ((x-1)+lceilfrac{n}{x} ceil-1=x+lceilfrac{n}{x} ceil-2ge 2sqrt 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
    ",min(calc(m),calc(m+1)));
    	}
    	return 0;
    }
    

    CF1426D

    题意简述

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

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

    (2le nle 2cdot10^5,-10^9le a_ile 10^9,a_i eq 0)

    算法标签

    前缀和 hash

    算法分析

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

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

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

    时间复杂度 (O(nlog 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
    ",sum);
    	return 0;
    }
    

    CF1426E

    题意简述

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

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

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

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

    (1le nle 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
    ",ans1,ans2);
    	return 0;
    }
    

    CF1426F

    题意简述

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

    (3le |s|le 2cdot10^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}inom{y}{i}2^{y-i}cdotleft(x+i ight) ]

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

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

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

    [xcdot3^y+sum_{i=1}^{y}inom{y}{i}2^{y-i}i ]

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

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

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

    [xcdot3^y+ysum_{i=1}^{y}inom{y-1}{y-i}2^{y-i} ]

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

    [xcdot3^y+ysum_{i=0}^{y-1}inom{y-1}{i}2^{i} ]

    最终答案为:

    [xcdot3^y+ycdot3^{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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    软件原则
    Optional
    雪花算法原理
    AOP
    trycatchfinally
    lambda表达式
    BeanUtils属性转换工具
    @Transactional 失效
    用户线程&&守护线程
    卡顿问题
  • 原文地址:https://www.cnblogs.com/ZigZagKmp/p/13758252.html
Copyright © 2011-2022 走看看