Codeforces Round #523 (Div. 2)总结

A题:

水题,如果k能整除n,则输出k/n,否则输出k/n+1.

代码:

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn=1e5+9;
int main(){
	long long i,j,k,n;
	cin>>n>>k;
	long long ans=0;
	if(k%n==0){
		ans=k/n;
	}
	else{
		ans=k/n+1;
	}
	cout<<ans<<endl;
}

B题:

刚开始的时候脑袋没转过来,后来发现这道题,我把它每一列按方块数从小到大排序,然后遍历每一列,开一个标记pl代表前面所有列中保存的方块中处于最高位置的块的行数,然后对于正准备保存方块的这一列,如果能方块数>pl,则保存位于pl+1位置的方块,否则仍然保存高度为pl的方块,需要记住的是我们每次保存完之后要ans+=a[i]-1(最后一列不需要这样处理),对于最后一列,我们需要做不同的处理,若此时pl==最后一列的方块数,ans+=pl-1,否则ans+=pl.

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn=1e5+9;
#define ll long long
ll a[maxn];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	ll i,j,k,n,m;
	cin>>n>>m;
	for(i=0;i<n;i++){
		cin>>a[i];
	}
	sort(a,a+n);
	if(n==1){
		cout<<0<<endl;
		return 0;
	}
	ll mx=0,ans=0;
	for(i=0;i<n-1;i++){
		if(a[i]>mx){
			ans+=a[i]-1;
			mx++;
		}
		else{
			ans+=a[i]-1;
		}
	}
	if(mx!=a[n-1])
	ans+=mx;
	else
	ans+=mx-1;
	cout<<ans<<endl;

}

C题,一道DP题,董佬,江佬太强了,30分钟写出来。仔细想想,首先,很容易能发现,对于每个位置上的数,有效的因子不会超过它的位置数,并且这个位置上的数贡献的每个方案里的元素数不会超过它最大的因子,然后我们能发现这个问题的可以分解成若干个性质相同的子问题,因为我们可以由前i个数贡献的方案数得到前i+1个数的方案数,它们是可以递推的,因此我们可以考虑用DP来解决这个问题。不妨开一个二维数组DP[i][j],代表前i个数能组成含有j个元素的方案数,DP[i][j]=DP[i-1][j]+DP[i-1][j-1]。至于我们是怎么得到这个状态转移方程的,首先既然我们要计算DP[i][j],那么j必定是a[i]的一个因子,那么它必定能与前面的数进行组合,所以我们只要把前i-1个数能组成j个元素的方案数加上前i-1个数能组成j-1个元素的方案数就行了,然后我们继续思考,发现其实对于i这一维我们是可以通过for循环来压缩掉的,因此我们其实只需要开一个一维数组就行了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
const long long maxn=1e6+9;
long long  a[maxn],f[maxn];
bool cmp(long long a,long long b){
    return a>b;
}
const long long mod=1e9+7;
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
   // cout<<123<<endl;
    long long i,j,k,n;
    cin>>n;
    for(i=0;i<n;i++){
        cin>>a[i];
    }
    vector<long long>vec[maxn];
    for(i=0;i<n;i++){
        for(j=1;j<=sqrt(a[i]);j++){
            if(a[i]%j==0){
            vec[i].push_back(j);
            if(j*j!=a[i])
                vec[i].push_back(a[i]/j);
            }
        }
        sort(vec[i].begin(),vec[i].end(),cmp);
    }
    long long ans=0;
    f[0]=1;
    for(i=0;i<n;i++){
        long long len=vec[i].size();
        for(j=0;j<len;j++){
            f[vec[i][j]]=(f[vec[i][j]]+f[vec[i][j]-1])%mod;
            ans=(ans+f[vec[i][j]-1])%mod;
        }
    }
    cout<<ans<<endl;
}

D题:贪心题。题意就是我要把所有的TV shows看完,但如果两个电视节目的时间段产生交集的话就得用两个电视观看,租一个电视的花费是x,观看电视节目每分钟需要花费y,问最少花费多少能看完所有节目。

这道题我们的最优策略就是如果两个观看区间能连起来并且连接所需的花费少于另租一台电视所需的花费的话就将两个区间连起来,否则不连。我们通过二分查找来实现这种策略。首先开一个set数组,set数组储存放入的r,每次想放一个区间的r值到set容器里时,都在set容器里找一下第一个比这个区间的l值小的r值,如果能找到,并且连接的花费小的话就将这个区间的r值放进去,并将找到的那个r值删去(若set容器里具有相同r值的区间的个数为1的话删除,否则减一,由于set容器里的每个数是唯一的,所以我们需要开一个map来记录每个数的个数),否则直接将这个区间的r值放进去。

代码:

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn=1e5+9;
const int mod=1e9+7;
struct node{
    int l,r;
}a[maxn];
bool cmp(node a,node b){
    if(a.l!=b.l)return a.l<b.l;
    return a.r>b.r;
}
int main(){
    int i,j,k,n;
    long long x,y;
    scanf("%d%lld%lld",&n,&x,&y);
    set<int>p;
    for(i=0;i<n;i++){
        scanf("%d%d",&a[i].l,&a[i].r);
    }
    sort(a,a+n,cmp);
    long long sum=0;
    map<int ,int >mp;
    p.insert(a[0].r);
    mp[a[0].r]++;
    sum+=x+(a[0].r-a[0].l)*y;
    for(i=1;i<n;i++){
        auto it=p.lower_bound(a[i].l);
        if(it==p.begin()){
            sum=(sum+x+(a[i].r-a[i].l)*y)%mod;
            mp[a[i].r]++;
            p.insert(a[i].r);
        }
        else{
            it--;
            if(a[i].l>*it&&(a[i].l-*it)*y<=x){
                sum=(sum+(a[i].r-*it)*y)%mod;
                p.insert(a[i].r);
                mp[a[i].r]++;
                mp[*it]--;
                if(!mp[*it])p.erase(*it);
            }
            else{
                sum=(sum+x+(a[i].r-a[i].l)*y)%mod;
                p.insert(a[i].r);
                mp[a[i].r]++;
            }
        }
    }
    printf("%lld\n",sum);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值