week 2 DAY 2:

Made Up - SMUOJ

给定三个长度为N的序列:A=(A1,A2,…,AN), B=(B1, b2,…,BN), C=(C1,C)
2,…,CN),由1到N(含)之间的整数组成。有多少对整数(i,j)在1到N(包括间满足 ?

题解:

只需要找到以B为数组,c为下标的数中有没有出现在A数组中就行,那么我们开一个数组记录A数组中出现的数字个数是多少,即可;

#include<bits/stdc++.h>
using namespace std;
#define int long long
int b[100005];
int a[100005];
signed main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int x;
		cin>>x;
		a[x]++;
	}	
	for(int i=1;i<=n;i++){
		cin>>b[i];
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		int c;
		cin>>c;
		ans+=a[b[c]];	
	}
	cout<<ans<<endl;
	return 0;
}

Red and Green Apples - SMUOJ

你要吃X个红苹果和Y个青苹果。
A个红苹果的美味度为p1 p2,…,pA

B个青苹果的美味度为q1,q2,…,qB

C个无色苹果的美味度为r1 r2,…,rC。
在吃无色的苹果之前,你可以把它涂成红色或绿色,这样它就分别算作红苹果或绿苹果了。
从上面的苹果中,你将选择吃的苹果,同时使所吃苹果的美味总和尽可能大。
找出在为0个或多个无色苹果上色时所能达到的食用苹果的最大可能的美味总和。

题解:

先对A,B数组从大到小进行排序,取A数组中的前a个数字,将其累加,再取B数组中的前b个数字,在A累加后的基础上将其再累加,在选取出来a个A数组中的数,b个B数组中的数中,用他们的最小值依次换取r数组中的最大值(如果一个数组中的数全部被换过了,就比较另一个数组是否符合换的条件)

推荐样例:

2 2 3 3 3

1 2 3

4 5 6

7 8 9

答案:30;

#include<bits/stdc++.h>
using namespace std;
#define int long long

int aa[100005],bb[100005],r[100005];
bool cmp(int a,int b){
	return a>b;
}
signed main(){
	int x,y,a,b,c;
	cin>>x>>y>>a>>b>>c;
	for(int i=1;i<=a;i++){
		cin>>aa[i];
	}
	for(int j=1;j<=b;j++){
		cin>>bb[j];
	}
	for(int i=1;i<=c;i++){
		cin>>r[i];
	}
	
	int ans=0;
	sort(aa+1,aa+1+a,cmp);
	sort(bb+1,bb+1+b,cmp);
	sort(r+1,r+1+c,cmp);
	for(int i=1;i<=x;i++)
	{
		ans+=aa[i];
	}
	for(int i=1;i<=y;i++){
		ans+=bb[i];
	}

	for(int i=1;i<=c;i++){
		//	if((aa[x]>=r[i])&&(bb[y]>=r[i])||(x==0&&y==0))break;
		if(x!=0&&aa[x]<r[i]&&(y==0||(bb[y]>=aa[x]))){
			ans-=aa[x];
			ans+=r[i];

			x--;
		}
		
		else if(y!=0&&bb[y]<r[i]&&(x==0||(bb[y]<aa[x]))){
			//cout<<1<<endl;
			ans-=bb[y];
			ans+=r[i];
			y--;
		}
	}
	cout<<ans<<endl;
	return 0;
}

H and V - SMUOJ

题意:有H行,w列, ' . '点表示白块,'#' 表示黑块,可以将任意行任意列的块标记为红色,问有几种放发可以使得最后的“”#“个数敲好是k个;

题解:二进制枚举做法

题外话;(今天终于理解二进制枚举了,就是一个集合,里边有n个元素,那么他的子集也就是不同(选或则不选)的组合)高中就知道是2的n次方,那么将1,到2的n次方之间的二进制数就是所有的可能,这也就是所谓的二进制枚举,

就是有h行m列,那么就有2^n*2^m种可能性;

那么我们就先枚举1~2 的n次方就是1<<n位;

代码:

#include<bits/stdc++.h>
using namespace std;
char c[10][10];
int h,w,k;
int num=0;
int ans=0;
bool sc(int x,int y){
	int res=0;
	for(int i=0;i<h;i++){
		for(int j=0;j<w;j++){
			if((x>>i&1||y>>j&1)&&c[i][j]=='#')
			res++;
		}
	}
	if(num-res==k)return 1;
	
	else return 0;
}
int main(){
	cin>>h>>w>>k;
	for(int i=0;i<h;i++){
		for(int j=0;j<w;j++){
			cin>>c[i][j];
			if(c[i][j]=='#')num++;
		}
	}
	
	for(int i=0;i<1<<h;i++){
		for(int j=0;j<1<<w;j++){
			ans+=sc(i,j);
		}
	}
	cout<<ans<<endl;
    return 0;
}

Rem of Sum is Num - SMUOJ

题目:函数f(x) 代表一个数的正数因数的个数:(数论+逆向思维+枚举因子个数)

求:

输入N;

1<N<=1e7;

拓展(和这题没关系):N=p1^a*p2^b*p3^c....*pn^z;(p为质数)N的质因子个数为(a+1*(b+1)*(c+1)*......(z+1);

时间复杂度是O(nlogn)

好神奇的解法!

欧拉筛O(n*logn)模版:

for(int i=2;i<=n;i++){
		if(f[i]==0)a[++cnt]=i;
		for(int j=1;j<=cnt;j++){
			if(i*a[j]>n)break;
			f[i*a[j]]=1;
			if(i%a[j]==0)break;
		}
	}

从1迅速到n,然后再写一个循环,n有几次被数x累加到n,就累加依次,时间复杂度竟然书nlogn;

为什么是nlogn?logn是因为他每次+i,最多只会执行n/i次,所以是一个调和函数,当n趋于无穷时趋近于ln(n);就直接携程nlogn吧..^-^.

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 6;
int f[10000007];
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=i;j<=n;j+=i){
			f[j]++;
		}
	}
	int sum=0;
	for(int i=1;i<=n;i++){
		sum+=f[i]*i;
	}
	cout<<sum<<endl;
	return 0;
}

 思路:区间和用前缀和处理;

题目要求子区间的和求余等于子区间的个数,那么就是(sum[r]-sum[l])%k==r-l;

现行就是sum[r]-r=sum[l]-l+a*k;

(sum[r]-r)%k=(sum[l]-l)%k;

所以我们就只需要找出在区间长度k时,答案就是相同的f(x) [ f(x):sum[k]-k]出现的次数之和;

所以我们要先处理长度为1-k-1的框中有几个(f(x)),然后框框后移,继续查找后边还有无出现相同的f(x);

#include <bits/stdc++.h>
using namespace std;
#define int  long long
#define ll long long
#define endl '\n'
const int N  = 2e5+10;
int x,f[N];
map<int,int> mp;
void solve()
{
	int n,k;
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>f[i];
		f[i] += f[i-1];
	}
	for(int i=1;i<=n;i++)
		f[i] = (f[i] - i)%k;
	int pos = min(n,k-1);
	ll sum = 0;
	// 首先处理前pos个元素(即最多到k-1),因为它们的模k结果可能在后面再次出现  
	for (int i = 0; i <= pos; i++) {  
		// 如果当前前缀和的模k结果已经在map中,则增加sum  
		sum += mp[f[i]];  
		// 更新map中当前前缀和的模k结果的出现次数  
		mp[f[i]]++;  
	}  
	
	// 处理剩余的元素(从pos+1到n),使用滑动窗口的思想  
	for (int i = pos + 1; i <= n; i++) {  
		// 从map中移除窗口最左边的元素(即i-k位置的元素)的模k结果  
		mp[f[i - k]]--;  
		// 如果当前前缀和的模k结果已经在map中,则增加sum  
		sum += mp[f[i]];  
		// 更新map中当前前缀和的模k结果的出现次数  
		mp[f[i]]++;  
	}  
	
	// 输出满足条件的元素个数  
	cout << sum << endl;  
}  
}
signed main()
{
	ios::sync_with_stdio(false);
	solve();
	return 0;
}

Moving Piece - SMUOJ

题意:一个长度为n(n<=5000)的数组C,还有一个1-n的全排列p,你可以随意选择一个元素开始游戏,然后跳至多k次,比如现在在x,下一次就在P[x],问跳过的C之和最大是多少。

思路:n<=5000,可以对于每一个点,假设从它开始,然后处理它走i步的收益是多少,

max_element(),返回的是第一个最大的元素的位置,相反的有min_element();

#include <iostream>//输入输出
#include<algorithm>//max_element(),min_element()
#include <vector>
using namespace std;

void main() {
	//max_element用于返回最大值的下标,*max_element用来取最大值
    int a[5] = { 2, 3, 5, 4, 5 };
    cout << (*max_element(a, a + 5)) << ' ';
    cout << (*min_element(a, a + 5)) << endl;
    cout << max_element(a, a + 5)-a << ' ';
    cout << min_element(a, a + 5)-a << endl;
    //通过数组a的地址初始化,注意地址是从0到5(左闭右开区间)
    vector<int> b(a, a + 5);
    cout << (*max_element(b.begin(), b.begin() + 5)) << ' ';
    cout << (*min_element(b.begin(), b.begin() + 5)) << endl;
    cout << max_element(b.begin(), b.begin() + 5)- b.begin() << ' ';
    cout << min_element(b.begin(), b.begin() + 5) - b.begin() << endl;
}

题解:遍历n个节点,寻找每个节点,寻找每个节点一周的代价,;

若代价为正,就寻找从i带开始,能有几个循环,就累加几次y(一次循环后得到的代价),加上当前所在的代价的值,然后和后面的比较谁更大,更新所得的值。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int p[5005],c[5005];
int ans=1e-15;
signed main()
{
	int n,k;
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>p[i];
	}
	for(int j=1;j<=n;j++)cin>>c[j];
	for(int i=1;i<=n;i++){
		int cut=i;
		int be=i;
		vector<int>s;
		s.push_back(cut);
		int y=c[cut];
		cut=p[cut];
		while(cut!=be){
			s.push_back(cut);
			y+=c[cut];
			cut=p[cut];//一定会循环
		}
		int  t=0;
		for(int i=0;i<s.size();i++){
			t+=c[s[i]];
			if(i>=k)break;
			int temp=t;
			if(y>0)temp+=(k-i-1)/s.size()*y;
			ans=max(ans,temp);
		}
	}
	if(ans==0)cout<<*max_element(c+1,c+1+n);
	else cout<<ans<<endl;
	return 0;
}

  • 16
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值