zoukankan      html  css  js  c++  java
  • AtCoder abc159

    没想到昨天的ABC竟是如此之水。而我却没打,真是太可惜了/kk

    AtCoder比赛页面传送门

    A - The Number of Even Pairs

    AtCoder题目页面传送门

    (n+m)个整数,其中(n)个是偶数,(m)个是奇数。求有多少种不同的选(2)个不同的数的方案,使得这(2)个数之和是偶数。

    显然,(2)个数之和是偶数当且仅当它们奇偶性相同。选(2)个不同的偶数有(dfrac{n(n-1)}2)种方案,选(2)个不同的奇数有(dfrac{m(m-1)}2)种方案,共(dfrac{n(n-1)}2+dfrac{m(m-1)}2)种方案。

    下面是AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
    	int n,m;
    	cin>>n>>m;
    	cout<<n*(n-1)/2/*选2个不同的偶数*/+m*(m-1)/2/*选2个不同的奇数*/;
    	return 0;
    }
    

    B - String Palindrome

    AtCoder题目页面传送门

    定义一个长度为奇数的字符串(a)是强回文的当且仅当(a,a_{1sim frac{n-1}2},a_{frac{n+3}2sim |a|})(3)个字符串都是回文的。给定一个长度为奇数的字符串(a),判断它是否强回文。

    根据强回文的定义判断即可。

    下面是AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=99;
    int n;
    char a[N+5];
    bool pal(int l,int r){//[a[l~r]是回文的]
    	while(l<=r){
    		if(a[l]!=a[r])return false;
    		l++;r--;
    	}
    	return true;
    }
    int main(){
    	cin>>a+1;
    	n=strlen(a+1);
    	puts(pal(1,n)&&pal(1,n-1>>1)&&pal(n+3>>1,n)?"Yes":"No");//定义 
    	return 0;
    }
    

    C - Maximum Volume

    AtCoder题目页面传送门

    给定一个整数(x)。求所有各棱长为实数且和为(x)的长方体中最大的体积是多少。

    原问题即给定(x),求(maxlimits_{a+b+c=x}{abc})。根据和定差小积大,当(a=b=c=dfrac x3)时,(abc)最大为(left(dfrac x3 ight)^3)

    下面是AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
    	double x;
    	cin>>x;
    	printf("%.100lf",pow(x/3,3));
    	return 0;
    }
    

    D - Banned K

    洛谷题目页面传送门 & AtCoder题目页面传送门

    给定(n)个整数,第(i)个数为(a_i)。求(forall iin[1,n]),不能选(a_i)的情况下,有多少种不同的选(2)个相等的数的方案。

    (ninleft[3,2 imes10^5 ight],a_iin[1,n])

    由于(a_iin[1,n]),所以我们可以直接把数们放进桶里,(buc_i=sumlimits_{j=1}^n[a_j=i])。这样,(forall iin[1,n])(i)的答案显然是(sumlimits_{j=1}^ndfrac{(buc_j-[a_i=j])(buc_j-[a_i=j]-1)}2)。但这样时间复杂度显然是(mathrm O!left(n^2 ight))。于是考虑先算出不带“不能选某数”的限制时的答案(ans=sumlimits_{i=1}^ndfrac{buc_i(buc_i-1)}2)(mathrm O(n)),然后(forall iin[1,n]),从(ans)里减去必须选(a_i)时的方案数,答案为(ans-(buc_{a_i}-1))。时间复杂度(mathrm O(n))

    下面是AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    const int N=200000;
    int n;
    int a[N+1];
    int buc[N+1];
    signed main(){
    	cin>>n;
    	for(int i=1;i<=n;i++)cin>>a[i],buc[a[i]]++/*装进桶*/;
    	int ans=0;
    	for(int i=1;i<=n;i++)ans+=buc[i]*(buc[i]-1)/2;//不带限制时的答案 
    	for(int i=1;i<=n;i++)cout<<ans-(buc[a[i]]-1)<<"
    ";
    	return 0;
    }
    

    E - Dividing Chocolate

    洛谷题目页面传送门 & AtCoder题目页面传送门

    给定一个(n imes m)的01字符矩阵(a)。求最少需要多少次水平或竖直切割,使得切出的每一块都包含不超过(s)( exttt1)

    (nin[1,10],min[1,1000])

    看到(n)如此之小,不难想到暴力枚举水平切割的(2^{n-1})种状态。对于每一种状态,从左往右贪心,每到一列就将水平切割切成的每一块当前的( exttt1)的个数增加若干,如果至少有(1)块当前的( exttt1)的个数超过了(s),便在本列与上一列之间竖直切割一刀,如果仍然超过,那么此种水平切割状态就不能要。贪心的正确性显然。对于每种水平切割状态,如果能要的话更新答案即可。时间复杂度(mathrm O(2^nnm))

    下面是AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    const int inf=0x3f3f3f3f;
    const int N=10,M=1000;
    int n,m,s;
    char a[N+1][M+5];
    int main(){
    	cin>>n>>m>>s;
    	for(int i=1;i<=n;i++)cin>>a[i]+1;
    	int ans=inf;
    	for(int i=0;i<1<<n-1;i++){//暴力枚举2^(n-1)种水平切割状态 
    		vector<int> pos;//水平切割处 
    		pos.pb(0);
    		for(int j=1;j<n;j++)if(i&1<<j-1)pos.pb(j);
    		pos.pb(n);
    		int now=pos.size()-2;//此水平切割状态当前切割次数 
    		vector<int> cnt(pos.size()-1,0);//水平切割的每一块当前'1'的个数 
    		for(int j=1;j<=m;j++){//从左往右贪心 
    			bool ok=true; 
    			vector<int> cnt0(pos.size()-1,0);//本列水平切割的每一块'1'的个数 
    			for(int k=0;k<cnt.size();k++){//枚举水平切割的每一块 
    				for(int o=pos[k]+1;o<=pos[k+1];o++)cnt0[k]+=a[o][j]^48;
    				if(cnt0[k]>s)goto label_end;//不能要 
    				if(cnt[k]+cnt0[k]>s)ok=false;//需要竖直切割 
    			}
    			if(ok)for(int k=0;k<cnt.size();k++)cnt[k]+=cnt0[k];//不竖直切割
    			else now++,cnt=cnt0;//竖直切割 
    		}
    		ans=min(ans,now);//更新答案 
    	label_end:;
    	}
    	cout<<ans;
    	return 0;
    }
    

    F - Knapsack for All Segments

    洛谷题目页面传送门 & AtCoder题目页面传送门

    给定(n)个整数,第(i)个数为(a_i)。定义(f(l,r))表示([l,r])内和为(m)的集合的数量。求(sumlimits_{i=1}^nsumlimits_{j=i}^nf(i,j))。答案对(998244353)取模。

    (n,m,a_iin[1,3000])

    考虑算贡献法。对于某个集合({a_{x_i}mid iin[1,k]}),其中(x_1<x_2<cdots<x_k),区间([l,r])包含它当且仅当(lin[1,x_1],rin[x_k,n]),所以此集合对答案的贡献为(x_1(n-x_k+1))

    看到“和为某数”一类的问题,不难想到类似背包的值域DP。设(dp_{i,j})表示考虑到第(i)个数,所有和为(j)的集合最左边的位置之和。边界是(dp_{0,i}=0),目标是(sumlimits_{i=1}^negin{cases}0&m<a_i\i(n-i+1)&m=a_i\dp_{i-1,m-a_i}(n-i+1)&m>a_iend{cases})(枚举和为(m)的集合最右边的位置贡献答案),状态转移方程是(dp_{i,j}=dp_{i-1,j}+[j=a_i]i+egin{cases}dp_{i-1,j-a_i}&jgeq a_i\0&j<a_iend{cases})。时间复杂度(mathrm O(nm))

    另外,此状态转移方程与01背包极其相似,可以用类似01背包的方式将DP数组压成一维,并在每次用(a_i)更新(dp')之前及时将(i)作为和为(m)的集合最右边的位置贡献答案。

    下面是AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=998244353;
    const int N=3000,M=3000;
    int n,m;
    int a[N+1];
    int dp[M+1];//dp' 
    int main(){
    	cin>>n>>m;
    	for(int i=1;i<=n;i++)cin>>a[i];
    	int ans=0;
    	for(int i=1;i<=n;i++){
    		if(m==a[i])(ans+=1ll*i*(n-i+1)%mod)%=mod;
    		else if(m>a[i])(ans+=1ll*dp[m-a[i]]*(n-i+1)%mod)%=mod;//更新答案 
    		for(int j=m;j>=a[i];j--)(dp[j]+=dp[j-a[i]]+(j==a[i]?i:0))%=mod;//转移 
    	}
    	cout<<ans;
    	return 0;
    }
    
  • 相关阅读:
    Luogu P4727 [HNOI2009]图的同构记数
    ARC 101 E
    JSOI2019 Round2 游记
    JSOI2019 Round1(十二省联考)游记
    Technocup 2019
    Codeforces Round #533 (Div. 2)比赛总结
    学习链接
    2018.12.29-2018.1.9安师大附中集训
    关于考试
    NOIP2018提高组 游记
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/AtCoder-abc159.html
Copyright © 2011-2022 走看看