今日训练
我吐了
花了好久学的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;
}