牛客练习赛60补题(待更新)(A、二进制 B、思维 C、DP D、拓展欧几里得算法)

目录

    • A、大吉大利
    • B、三角形周长和
    • C、操作集锦
    • D、斩杀线计算大师

A、大吉大利

链接:https://ac.nowcoder.com/acm/contest/4853/A
一道挺普通的二进制题目,和洛谷之前做的谔运算有点像,可能还更简单一点。

#include 
using namespace std;
typedef long long ll;
ll counter1[128]={0};
ll counter2[128]={0};//其实不用开这么大,32就够了。。下面循环次数也是。
int main()
{
	ll n;
	cin>>n;
	for(ll i=0;i<n;i++){
		ll tmp;
		cin>>tmp;
		for(ll j=0;tmp;j++){
			counter1[j]+=(tmp&1);
			tmp>>=1;
		}
	}
	for(ll i=0;i<128;i++){
		counter2[i]=counter1[i]*counter1[i];
	}
	for(ll i=0;i<127;i++){
		counter2[i+1]+=counter2[i]/2;
		counter2[i]=counter2[i]%2;
	} 
	ll ans=0;
	for(ll i=127;i>=0;i--){
		ans<<=1;
		ans+=counter2[i];
	}
	cout<<ans<<endl;
	return 0;
}

B、三角形周长和

思维题(弱项),任意选取两个点,则这两个点(记为A、B)和另外的(n-2)个点可以分别形成(n-2)个三角形,那么,周长和当中,将包括(n-2)个|AB|,依照这个原理可以算出来(太牛逼了orz)。

#include 
#include 
#include 
using namespace std;
typedef long long ll;
const ll maxn=1e3+10;
const ll mod=998244353;
ll point[maxn][2];
ll cal(int i,int j){
	return abs(point[i][0]-point[j][0])+abs(point[i][1]-point[j][1]);
}

int main()
{
	ll n;
	scanf("%lld",&n);
	for(ll i=0;i<n;i++){
		scanf("%lld %lld",&point[i][0],&point[i][1]);
	}
	ll ans=0;
	for(ll i=0;i<n;i++){
		for(ll j=i+1;j<n;j++){
			ans=(ans%mod+(n-2)*cal(i,j)%mod)%mod;
		}
	}
	printf("%lld\n",ans%mod);
	return 0;
}

C、操作集锦

一开始往排列组合方向去想了。。发现想不通,可能还是抓不住排列组合的特点吧。
这道用dp做,需要二维数组。dp[i][j]表示在前i个字符中,组成长度为j的组合的个数,状态转移方程(不考虑重复)也比较容易地出来:dp[i][j] 为 dp[i-1][j](表示前面得出的组成长度为j的组合的数目)+dp[i-1][j-1](表示前面的已经组成了j-1长度的组合再添加上目前位置的字符所形成的组合的数目),那重复的怎么办呢?
先分析下:重复是从哪里来的呢?其实就是:假设目前该位上的字符是b,则,从dp[i-1][j-1]生成过来的代表的字符串结尾就是b,若前面也有哪一位(记为pre)上的字符是b,因为目前位置在它的后面,所以dp[i-1][j]就含有和现在要生成的字符串相互重复的(因为新生成的结尾也会是b,dp[i-1][j-1]也会含有和dp[pre-1][j-1]相同的组合,则可能会生成重复的组合)。因此,需要一个pre数组,以字母为键,记录该字符出现的上一个位置,方便查重。

#include 
using namespace std;
const int maxn=1e4+10;
typedef long long ll;
const ll mod=1e9+7;
char s[maxn];
ll dp[maxn][maxn];
int pre[maxn];
ll k,n;
void pr(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cout<<dp[i][j]<<" ";
		}
		cout<<endl;
	}
	cout<<endl<<endl;
}

int main()
{
	scanf("%lld %lld",&n,&k);
	scanf("%s",s+1);
	dp[0][0]=1; 
	for(int i=1;i<=n;i++){
		dp[i][0]=1;
		for(int j=1;j<=i;j++){
			dp[i][j]=(dp[i-1][j-1]+dp[i-1][j]+mod)%mod;
			if(pre[s[i]-'a']){
				dp[i][j]-=dp[pre[s[i]-'a']-1][j-1];
			}
		}
	//	pr();
		pre[s[i]-'a']=i;
	}
	printf("%lld",(dp[n][k]+mod)%mod);
	return 0;
}

D、斩杀线计算大师

就是考的扩展欧几里得算法,需要注意的就是如果算出来x是负的怎么办的处理,不多说,多复习就行。

#include 
using namespace std;
typedef long long ll;
ll a,b,c,k; 
void extend_gcd(ll a,ll b,ll&x,ll&y)
{
	if(b==0)
	{
		x=1;
		y=0;
		return; 
	}
	extend_gcd(b,a%b,x,y);
	ll t=x;
	x=y;
	y=t-(a/b)*y;
}

ll gcd(ll a,ll b){
	return b==0?a:gcd(b,a%b);
}

int main()
{
	scanf("%lld%lld%lld%lld",&a,&b,&c,&k);
	for(int i=0;i<=k/c;i++){
		ll cc=k-i*c;
		ll x,y;
		extend_gcd(a,b,x,y);
		ll d=gcd(a,b);
		if(cc%d!=0) continue;
		x=cc/d*x,y=cc/d*y;
		x=(x%(b/d)+(b/d))%(b/d);
		y=(cc-x*a)/b;
		if(x>=0&&y>=0){
			printf("%lld %lld %lld",x,y,i);
			return 0;
		}
	}
	return 0;
 } 

ps.有个奇葩的事,暴力竟然能过?

#include 
#include 
using namespace std;
typedef long long ll;
ll a,b,c,k;

int main()
{
	scanf("%lld %lld %lld %lld",&a,&b,&c,&k);
	if(k%a==0){
		printf("%lld %lld %lld",k/a,0,0);
	}
	else if(k%b==0){
		printf("%lld %lld %lld",0,k/b,0);
	}
	else if(k%c==0){
		printf("%lld %lld %lld",0,0,k/c);
	}
	else{
		for(ll i=k/a;i>=0;i--){
			ll left1=k-i*a;
			for(ll j=left1/b;j>=0;j--){
				ll left2=left1-j*b;
				if(left2%c==0){
					printf("%lld %lld %lld",i,j,(left2/c));
					return 0;
				}
			}
		}
	}
	return 0;
}

你可能感兴趣的:(动态规划,数论)