2021-05-01題解

今日训练
我吐了
花了好久学的dp,好像依旧很菜,鲨了我叭,球球了。

C 二分
题意:有n个时间炸弹,1.每次只能挑选一个+1,2.所有的炸弹-1,当有一个炸弹时间<0,则全部爆炸。求进行第一步最多多少次。相当于每一次一个炸弹不变,其余的-1;
思路:比赛时想的每次进行排序,给最小的+1,记录减小的值f,最小的值-f=-1终止,显然超时了;到最后也没想到怎么优化,最后去看题解,用二分二分二分!!离谱,合着我一点边都不沾,还试了那么多次!!!
正确思路:二分求一个中值,比此中值小的数的差和<=中值;ans=mid+1;将减得过程反看成加,eg:1 2 3 mid=3;3->0=3;1->0=1;2->0=2;每次不变的炸弹选1一次选2两次,第四次必爆炸;eg2:2 3 6 mid=5;2->0=2,3->0=3,6->0=6,2不变5-2=3次,3不变5-3=2次,此时2 3都变为0,所以第mid+1次必爆;eg3:4 4 10 11 mid=8;4->0=4,4->0=4,10->0=10;,4不变mid8-4=4次,4不变mid8-4=4次,此时4 4都变为0,所以第mid+1次必爆;
//思路还是有点蒙,有空再看看叭。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=200010;
int a[maxn];

int main()
{
	int t;
	cin>>t;
	int cas=1;
	while(t--)
	{
		int n;
		cin>>n;
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
			
		ll l=0,r=2e9;
		ll mid;
		ll ans=1;
		while(l<=r)
		{
			mid=(l+r)>>1;
			ll res=0;
			for(int i=1;i<=n;i++)
				if(a[i]<mid)
					res+=(mid-a[i]);
			if(res<=mid)
			{
				ans=mid+1;
				l=mid+1;
			}		
			else
				r=mid-1;	
		}
		
		printf("Case #%d: %lld\n",cas,ans);
		cas++;
	}
	
	return 0;	
}

D 树状数组补补补补 冲tm
E 线段相交
题意:给四个点,对应两条线段,求交点个数及坐标。
思路:数学思路丰富,代码实现0;向题解屈服,膜拜大佬。
在这里插入图片描述
在这里插入图片描述

#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;

struct point
{
	__int64 x, y;
};

struct fract
{
	__int64 z, m;

	__int64 gcd(__int64 a, __int64 b)
	{
		return b != 0 ? gcd(b, a%b) : a;
	}
	//这里还没看懂,貌似用了扩展欧几里得?
	void create(__int64 a, __int64 b = 1)
	{
		__int64 t = gcd(a, b);
		a = a / t;
		b = b / t;
		z = a;
		m = b;
		if (m < 0)
		{
			m = -m;
			z = -z;
		}
	}
	void print()
	{
		if (m == 1)cout << z;
		else cout << z <<"/"<< m;
	}
};
//判断斜率,v是两条直线的斜率大小比较
int cmp(__int64 v)
{
	if (v > 0)return 1;
	else if (v == 0)return 0;
	else return -1;
}

//判断三个点是否在一条直线上,return 0 即在一条直线 
__int64 det(point &a, point &b, point &c)
{
	return (b.x - a.x)*(c.y - a.y) - (b.y - a.y)*(c.x - a.x);
}

//判断点c是否在a,b之间,return 1在 
bool between(point &a, point &b, point &c)
{
	if (a.x - b.x != 0)
	{
		__int64 mn = min(a.x, b.x);
		__int64 mx = max(a.x, b.x);
		return mn <= c.x&&c.x <= mx;
	}
	else
	{
		//x=k的情况,斜率不存在,y1<yc<y2
		__int64 mn = min(a.y, b.y);
		__int64 mx = max(a.y, b.y);
		return mn <= c.y && c.y <= mx;
	}
}

// 0无交点,1有一个交点,2有无数交点
// 如果有1个交点,则p为交点位置
int Cross(point &a, point &b, point &c, point &d, fract &x, fract &y)
{
	int k1, k2, k3, k4;
	__int64 s1, s2, s3, s4;//对应三点两线的斜率的大小比较;
	k1 = cmp(s1 = det(a, b, c));
	k2 = cmp(s2 = det(a, b, d));
	k3 = cmp(s3 = det(c, d, a));
	k4 = cmp(s4 = det(c, d, b));

	//0^0=0,1^1=0,-1^-1=0;0^1=1,0^-1=-1,1^-1=-2;

	// 规范相交
	if ((k1 ^ k2) == -2 && (k3 ^ k4) == -2)
	{
		x.create(c.x * s2 - d.x * s1, s2 - s1);
		y.create(c.y * s2 - d.y * s1, s2 - s1);
		return 1;
	}

	__int64 mn1, mx1, mn2, mx2;
	if (a.x - b.x != 0)
	{
		mn1 = min(a.x, b.x);
		mx1 = max(a.x, b.x);
		mn2 = min(c.x, d.x);
		mx2 = max(c.x, d.x);
	}
	else
	{
		mn1 = min(a.y, b.y);
		mx1 = max(a.y, b.y);
		mn2 = min(c.y, d.y);
		mx2 = max(c.y, d.y);
	}

	// 不规范相交
	if (k1 == 0 && k2 == 0 && k3 == 0 && k4 == 0)
	{
		// 有无数交点
		//这里也没细看
		if (mn2 < mn1 && mn1 < mx2 || mn2 < mx1 && mx1 < mx2 ||
			mn1 < mn2 && mn2 < mx1 || mn1 < mx2 && mx2 < mx1 ||
			mn1 <= mn2 && mx2 <= mx1 || mn2 <= mn1 && mx1 <= mx2)
			return 2;
	}
	if (k1 == 0 && between(a, b, c))
	{
		x.create(c.x);
		y.create(c.y);
		return 1;
	}
	if (k2 == 0 && between(a, b, d))
	{
		x.create(d.x);
		y.create(d.y);
		return 1;
	}
	if (k3 == 0 && between(c, d, a))
	{
		x.create(a.x);
		y.create(a.y);
		return 1;
	}
	if (k4 == 0 && between(c, d, b))
	{
		x.create(b.x);
		y.create(b.y);
		return 1;
	}
	return 0;  
}

//判断两个点是否为同一个点
bool IsPoint(point &a, point &b)
{
	if (a.x == b.x && a.y == b.y) return true;
	else return false;
}

int main()
{
	int test, cas;//t样例数,c当前样例 
	point a, b, c, d;//四个点的结构体 
	fract x,y;
	int res;
	cin>>test;
	for (cas = 1; cas <= test; cas++)
	{
		cin >> a.x >> a.y >> b.x >> b.y;
		cin >> c.x >> c.y >> d.x >> d.y;

		//判断为点的情况 
		if (IsPoint(a, b) && IsPoint(c, d))
		{
			if (IsPoint(a, c))
			{
				cout << "1" << '\n';
				cout << a.x << " " << a.y << '\n';
			}
			else cout << "0" << '\n';

			continue;
		}
		if (IsPoint(a, b))
		{
			if (cmp(det(c, d, a)) == 0 && between(c, d, a))
			{
				cout << "1" << '\n';
				cout << a.x << " " << a.y << '\n';
			}
			else cout << "0" << '\n';

			continue;
		}
		if (IsPoint(c, d))
		{
			if (cmp(det(a, b, c)) == 0 && between(a, b, c))
			{
				cout << "1" << '\n';
				cout << c.x << " " << c.y << '\n';
			}
			else cout << "0" << '\n';

			continue;
		}

		res = Cross(a, b, c, d, x, y);
		if (res == 1)
		{
			cout << "1" << '\n';
			x.print();
			cout << " ";
			y.print();
			cout << '\n';
		}
		else if (res == 0) cout << "0" << '\n';
		else cout << "INF" << '\n';
	}

	return 0;
}

F看不懂,期望。
G 扩展欧几里得

借鉴

#include<string>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<iostream>
#include<cstdlib>
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b)
{
    return b==0?a : gcd(b,a%b);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ll r,b,k;
        scanf("%lld%lld%lld",&r,&b,&k);
        if(r>b)
            swap(r,b);
        ll ans=((b-1)-gcd(r,b))/r+1;
        if(k<=ans)
            printf("REBEL\n");
        else
            printf("OBEY\n");
    }
    return 0;
}

H 单调栈
题意:给一串字符串,出现两次及以上的字符删去只留一次,求删去后的最大化字符串;
eg:oba>pba;//从头开始ascii最大
思路:cnt[]把每一个字母出现次数存下来,k表示出现过多少个字母,利用一个单调栈,每一个字符与top相比,若大于top()&&top()的字母个数>0(在后面还有),top出栈,直至满足条件;vis[]表示该字母是否在栈里,存在为1,否则为0;

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<string>
#include<cstring>
#include<stack>
using namespace std;
typedef long long ll;
int cnt[30];
int vis[30];

int main()
{
	int n;
	cin>>n;
	while(n--)
	{
		string s,zz;
		cin>>s;
		memset(cnt,0,sizeof(cnt));
		memset(vis,0,sizeof(vis));
		
		for(int i=0;i<s.length();i++)
			cnt[s[i]-'a']++;
			
		int k=0;
		for(int i=0;i<26;i++)
			if(cnt[i]>0)k++;
			
		stack<char>ss;
		for(int i=0;i<s.length();i++)
		{
			cnt[s[i]-'a']--;//对应字母个数-1
			if(vis[s[i]-'a'])continue;//该字符已在栈里,直接删去i对应的这个字符
			while(!ss.empty()&&s[i]>ss.top()&&cnt[ss.top()-'a']>0)
			{
				vis[ss.top()-'a']=0;//出栈变0
				ss.pop();
			}
			ss.push(s[i]);//把该字符压入栈中
			vis[s[i]-'a']=1;//标记该字符在栈里为1
		}	
		for(int i=1;i<=k;i++)
		{
			zz+=ss.top();
			ss.pop();
		}
		reverse(zz.begin(),zz.end());//zz倒着
		cout<<zz<<endl;
	 } 
	return 0;
}

I 最大生成树
一点不会,全靠队友。

J dp
题意:给r*c的图求最长下降路;
思路:先对图的数值排序,再dp四个方向求最大值;

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
using namespace std;
typedef long long ll;
int dp[105][105];
struct yyqxwyb
{
	int x,y;
	int v;
};
yyqxwyb zz[10050];
int a[105][105];
bool cmp(yyqxwyb a1,yyqxwyb a2)
{
	if(a1.v!=a2.v)return  a1.v<a2.v;
	else if(a1.x!=a2.x)return a1.x<a2.x;
	else return a1.y<a2.y;
}

int main()
{
	int r,c;
	cin>>r>>c;
	int f=1;
	for(int i=1;i<=r;i++)
	{
		for(int j=1;j<=c;j++)
		{
			cin>>a[i][j];
			zz[f].v=a[i][j];
			zz[f].x=i;
			zz[f].y=j;
			f++;
		}
	}
	sort(zz+1,zz+f,cmp);
	for(int i=1;i<=r*c;i++)
	{
		int bz=a[zz[i].x][zz[i].y];
		if(a[zz[i].x+1][zz[i].y]<bz)dp[zz[i].x][zz[i].y]=max(dp[zz[i].x][zz[i].y],dp[zz[i].x+1][zz[i].y]);
		if(a[zz[i].x-1][zz[i].y]<bz)dp[zz[i].x][zz[i].y]=max(dp[zz[i].x][zz[i].y],dp[zz[i].x-1][zz[i].y]);
		if(a[zz[i].x][zz[i].y+1]<bz)dp[zz[i].x][zz[i].y]=max(dp[zz[i].x][zz[i].y],dp[zz[i].x][zz[i].y+1]);
		if(a[zz[i].x][zz[i].y-1]<bz)dp[zz[i].x][zz[i].y]=max(dp[zz[i].x][zz[i].y],dp[zz[i].x][zz[i].y-1]);
		dp[zz[i].x][zz[i].y]+=1;
	}
	
	int maxx=0;
	for(int i=1;i<=r*c;i++)
	{
	 	maxx=max(maxx,dp[zz[i].x][zz[i].y]);
	}
	cout<<maxx<<'\n';
	
	return 0;
}

K dp
题意:给一串数,找到单调递增求相邻的数最大公因数>1的最长子序列;
思路:把每个数的因子存下来,从头更新因子出现个数,因子出现次数最多的即为答案。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<string.h>
#include<vector>
using namespace std;
typedef long long ll;

const int maxn=100010;
int a[maxn];
int dp[maxn];
vector<int>v[maxn];

int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=1;i<=n;i++)
		v[i].clear();
		
	for(int i=1;i<=n;i++)
	{
		v[i].push_back(a[i]);
		for(int j=2;j<=(int)sqrt(a[i]);j++)
		{
			if(j*j==a[i])v[i].push_back(j);
			else 
			{
				if(a[i]%j==0)
				{
					v[i].push_back(j);
					v[i].push_back(a[i]/j);
				}
			}
		}
	}
	
	memset(dp,0,sizeof(dp));
	int maxx;
	for(int i=1;i<=n;i++)
	{
		maxx=0;
		//这里很绕,仔细看
		for(int j=0;j<v[i].size();j++)
		{
			dp[v[i][j]]++;
			maxx=max(maxx,dp[v[i][j]]);
		}
		for(int j=0;j<v[i].size();j++)
		{
			dp[v[i][j]]=maxx;
		}
	}
	
	maxx=0;
	for(int i=1;i<=a[n];i++)
		maxx=max(maxx,dp[i]);
	cout<<maxx<<'\n'; 
	
	return 0;
}

L
题意:4 8 15 16 23 42为一组,给你一串数,最少移除多少个可以变为好队列;
1.队列为空是好队列;
2.队列长度为6的倍数,且能化为k个4 8 15 16 23 42;
eg:4 4 8 8 15 16 15 16 23 42 23 42;是好队列,移除0个;
4 8 8 15 16 23 42,不是好队列,需要移除1个8;

#include <bitsdc++.h>
using namespace std;
typedef long long ll;
int a[500010];
int b[10];

int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	
	if(n<6)cout<<n<<'\n';
	else 
	{
		memset(b,0,sizeof(b));
		for(int i=1;i<=n;i++)
		{
			if(a[i]==4)b[1]++; 
			else if(a[i]==8)
			{
				if(b[1]>b[2])b[2]++;
			}
			else if(a[i]==15)
			{
				if(b[1]>=b[2]&&b[2]>b[3])b[3]++;
			}
			else if(a[i]==16)
			{
				if(b[1]>=b[2]&&b[2]>=b[3]&&b[3]>b[4])b[4]++;
			}
			else if(a[i]==23)
			{
				if(b[1]>=b[2]&&b[2]>=b[3]&&b[3]>=b[4]&&b[4]>b[5])b[5]++;
			}
			else if(a[i]==42)
			{
				if(b[1]>=b[2]&&b[2]>=b[3]&&b[3]>=b[4]&&b[4]>=b[5]&&b[5]>b[6])b[6]++;
			}
		}
		cout<<n-b[6]*6<<'\n';
	} 
	
	return 0;
 }

M 签到题

/*给一个数,除以2050,整除则求每一位的和,否则输出-1;*/
#include <bitsdc++.h>
using namespace std;
typedef long long ll;

ll t;
ll n, ans;

ll qiuweishu(ll x)
{
	ll sum = 0;
	while(x)
	{
		sum += (x % 10);
		x /= 10;
	} 
	return sum;
}

int main()
{
	cin >> t;
	for (int i = 1; i <= t; ++i)
	{
		cin >> n;
		ans = 0;
		ll temp;
		if(n % 2050 == 0){
			temp = n / 2050;
			ans = qiuweishu(temp);
			cout << ans << endl;
			continue;
		} 
		else cout << "-1" << endl;
	}
	
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值