【解题报告】CF DIV2 #ROUND 711 A~D

【解题报告】CF DIV2 #ROUND 711 A~D

比赛链接
虚拟赛,A了2题4000+名,还行

A.GCD Sum

思路
根据题意写就完事了,简单模拟
代码

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;

/*DATA & KEY
t 1 1e4
n 1 1e18
*/
int T;
LL gcd(LL a,LL b)
{
	return b ? gcd(b, a % b) : a;
}
void solve(int T)
{
	//NEW DATA CLEAN
	
	//NOTE!!!
	LL n;cin>>n;
	for(LL i=n;;i++)
	{
		LL tmp=i;LL s=0;
		while(tmp)
		{
			s+=tmp%10;
			tmp/=10;
		}
		if(gcd(i,s)>1)
		{
			cout<<i<<endl;
			break;
		}
	}
	return ;
	
}

int main()
{
	scanf("%d",&T);
	while(T--)solve(T);
	return 0;
}

B.Box Fitting

思路
二分+贪心+优先队列
每组和不超过W,问最少分几组
有点类似书分组问题,不过书分组那个分必须是连续分的。这个是可以随便分组的。
check函数的贪心利用优先队列就实现即可。
先w[N]从大到小到排,先把数值大的排了,然后利用优先队列维护所有组最大剩余空间,如果可以放进去就更新压入堆,并弹出原来的堆顶,反之开一个新组。(这里的思想有点像这题
但是写的时候WA了一波,原因是没有二分,只弄了check函数的贪心。

代码

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;

/*DATA & KEY
t 1 5e3
sum n 1 1e5
W 1 1e9
wi 1 1e6
*/
int T;
const int N=5e6+10;
LL w[N];
LL n,W;
bool check(LL mid)
{
	priority_queue<LL,vector<LL>,greater<LL>>heap;
	LL ans=0;
	for(int i=n;i>=1;i--)
	{
		if(heap.empty())
		{
			heap.push(w[i]);
			ans++;
		}
		else
		{
			if(W-heap.top()>=w[i])
			{
				int t=heap.top();
				heap.push(t+w[i]);
				heap.pop();
			}
			else
			{
				ans++;
				heap.push(w[i]);
			}
		}
	}
	return ans<=mid;
}
void solve(int T)
{
	//NEW DATA CLEAN
	//NOTE!!!
	cin>>n>>W;
	for(int i=1;i<=n;i++)cin>>w[i];
	sort(w+1,w+1+n);
	LL l=1,r=n;
	while(l<r)
	{
		LL mid=(l+r)>>1;
		if(check(mid))r=mid;
		else l=mid+1;
	}
	cout<<l<<endl;
}

int main()
{
	scanf("%d",&T);
	while(T--)solve(T);
	return 0;
}

C.Planar Reflections

思路
递归+记忆化,然后蜜汁写爆了样例RE
后来发现居然本地爆栈了!啊啊啊啊,交cf是可以直接AC的
后面本地加栈就没爆了
原本可以冲一波2000名的,淦

"C:\MinGW\bin\g++.exe" -Wall -std=c++11 -O2 -Wl,--stack=268435456

代码

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;

/*DATA & KEY
t 1 100
sum n 1 1000
sum k 1 1000
mod 1e9+7
*/
int T;
LL n,k;
const int mod=1e9+7,N=1e3+10;
LL f[N][N][2];
LL work(int k,int x,bool d)//k是衰变,x是第几层,d是方向
{
	if(f[k][x][d]!=0)return f[k][x][d];
	if(x==0||x==n+1)
	{
		return 1;
	}
	if(d==1)//原本向右
	{
		if(k>1)return f[k][x][d]=(work(k,x+1,1)+work(k-1,x-1,0))%mod;
		else return f[k][x][d]=work(k,x+1,1)%mod;
	}
	else
	{
		if(k>1)return f[k][x][d]=(work(k,x-1,0)+work(k-1,x+1,1))%mod;
		else return f[k][x][d]=work(k,x-1,0)%mod;
	}
}
void solve(int T)
{
	//NEW DATA CLEAN
	memset(f,0,sizeof f);
	//NOTE!!!
	cin>>n>>k;
	cout<<work(k,1,1)%mod<<endl;
}

int main()
{
	scanf("%d",&T);
	while(T--)solve(T);
	return 0;
}

D.Bananas in a Microwave

思路
题目没看清然后BFS写烂了,不是按操作使用总和计数,而是第一次出现这个数在第几个操作。仔细分析样例的话就能看出来的,比如第一个样例的6按两种方法计算是不一样的。

inputCopy

3 20
1 300000 2
2 400000 2
1 1000000 3

outputCopy

-1 -1 1 -1 -1 1 -1 -1 -1 3 -1 2 3 -1 -1 3 -1 -1 -1 3 

直接暴力和BFS都可以,大致思路想的差不多,但是又一次死在了时间复杂度的错误分析上。参看了这位大佬的博客恍然大悟,意识到自己是个憨憨。三重循环并不意味着时间复杂度就是 O ( n m 2 ) O(nm^2) O(nm2)这种

以操作步数为第一层循环,遍历0~m,如果已经出现过那就直接跳过,反之就扩展。时间复杂度为 O ( n m ∗ 2 ) O(nm*2) O(nm2)

另外写的时候不知道为什么ceilWA了,可以考虑手写上取整

(LL)(x+100000-1)/100000

代码

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
#define x first
#define y second
typedef long long LL;
typedef pair<int,int> PII;

/*DATA & KEY
n 1 200
m 2 1e5
ti 1 2
yi 1 m
type 2 xi/1e5 1 m
type 1 xi/1e5 (1,m]
*/
int T;
const int N=205,M=1e6+10,INF=0x3f3f3f3f;
struct Node{int op;LL x;int y;}a[N];
int n,m;
int num[M];
void solve()
{
	//NEW DATA CLEAN
	
	//NOTE!!!
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i].op>>a[i].x>>a[i].y;
	for(int i=1;i<=m;i++)num[i]=INF;
	for(int i=1;i<=n;i++)
	{
		for(int j=m;j>=0;j--)
		{
			if(num[j]==INF)continue;//如果没产生就不扩展
			LL tmp=j;
			for(int k=1;k<=a[i].y;k++)
			{
				if(a[i].op==1) tmp = tmp + (a[i].x + 100000 - 1) / 100000;//上取整
				else tmp = (tmp *a[i].x + 100000 - 1) / 100000;
		
				if(tmp>m)break;
				if(num[tmp]!=INF)break;
				num[tmp]=i;
			}
		}
	}
	for(int i=1;i<=m;i++)
		if(num[i]==INF)cout<<-1<<" ";
		else cout<<num[i]<<" ";
}

int main()
{
//	scanf("%d",&T);
//	while(T--)solve(T);
	solve();
	return 0;
}

看别的博客时候有人说这个问题像多重背包问题,确实挺像的,不过体积和物品价值挺难整的。不过这是一个好的联想。

n种物品----n种操作
每组物品s[i]----每个操作可以进行y[i]次
物品总价值最大---到达某个操作使用物品数最少
物品选择价值增加----操作选择数字增加  抽象出物品价值

反思

A:

简单模拟,涉及对循环变量操作记得tmp变量

B:

区分数列分段问题是连续的,这个问题是可以不连续的,考虑把大的放进去。放的下就分一组放不下就新增一组

C:

递归分治思想,记得本地加栈啊啊啊

D:

时间复杂度分析:对于同一集合内采用类似hash或者bool数组方式双重循环的时候时间复杂度往往远小于O(NM)而是单层O(N)
如果是有限集合内类似BFS的扩展,可以考虑每次遍历整个集合,出现过的则在此基础上遍历,反之跳过这样扩展的时间复杂度约为O(N)
多重背包联想:可以把物品抽象为操作,物品种类抽象为操作种类,物品数量抽象为允许操作次数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值