2022.3.10
蓝书
AcWing 109. 天才ACM
思路:使用倍增思想,如果普通二分的时间复杂度为logn,但当t非常小的时候相比倍增反而需要花费更多的时间,因为每次都是折半找,而倍增利用了二进制的思想,每一段都可以用2的几次方之和来表示,那么对于此题,长度的枚举先从1开始,初始化st和ed为0,只要len不为0的时候就继续做,每次做的时候考虑一下,如果len+ed<=n&&get(st,ed+len)<=t,说明这段扩展是合法的,我们可以尝试将len*2再进行扩展,否则不合法,需要将len/2,直至len=0为止,此时已经找到了一段符合题目要求的段了,cnt++,并且让st=ed,同时长度恢复为1,继续下一段的枚举。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=5e5+10,INF=1e8;
ll a[N], b[N],t;
int n, m;
ll get(int l,int r)
{
ll ans = 0;
int k = 0;
for (int i = l; i < r;i++)
b[k++] = a[i];
sort(b, b + k);
for (int i = 0; i <k &&i<m;i++,k--)
{
ans += (b[i] - b[k - 1]) * (b[i] - b[k - 1]);
}
return ans;
}
int main()
{
int k;
scanf("%d",&k);
while(k--)
{
scanf("%d%d%lld", &n, &m, &t);
for (int i = 0; i < n;i++)
scanf("%lld", &a[i]);
int st = 0, ed = 0,cnt=0;
while(ed<n)
{
int len = 1;
while(len)
{
if(ed+len<=n&&get(st,ed+len)<=t)
{
ed += len;
len <<= 1;
}
else
len >>= 1;
}
st = ed;
cnt++;
}
printf("%d\n", cnt);
}
return 0;
}
AcWing 110. 防晒
思路:先将读入的牛和防晒霜从大到小排序,从大到小枚举,对于每头牛我们优先选择当前spf值较大的且符合的防晒霜给它用,假设当前牛可用的防晒霜有x,y。因为当前的防晒霜的spf值肯定大于当前牛的minspf,所以x,y也一定大于之后的牛的minspf,这样就会有三种情况,1.后面的牛都可以用x,y。2.后面的牛都不可以用x,y。3.可以用x,不可以用y。那么此时选择spf值较大的防晒霜显然更优。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+10,INF=1e8;
pii cow[N], spf[N];
int main()
{
int c, l;
scanf("%d%d", &c, &l);
for (int i = 1; i <= c;i++)
{
scanf("%d%d", &cow[i].first, &cow[i].second);
}
for (int i = 1; i <= l;i++)
{
scanf("%d%d", &spf[i].first, &spf[i].second);
}
sort(cow + 1, cow + 1 + c);
sort(spf + 1, spf + 1 + l);
int cnt = 0;
for (int i = 1; i <= c;i++)
for (int j = 1; j <= l;j++)
{
if(spf[j].second>0&&spf[j].first>=cow[i].first&&spf[j].first<=cow[i].second)
{
spf[j].second--;
cnt++;
break;
}
}
printf("%d\n", cnt);
return 0;
}
AcWing 111. 畜栏预定
思路:对于每头牛我们先判断一下当前有无可用的牛栏给它用,有就放进去同时更新一下结束的时间,否则牛栏+1。判断当前可用的牛栏我们可以建一个小根堆然后把每个牛栏的结束时间都放进去。
一开始看样例以为时间是递增的,结果后面一看发现错了,因为必须得知道每头牛的序号,所以改用结构体做,然后判断是否能放入牛栏的时候有些小细节需要注意。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=5e4+10,INF=1e8;
pii s[N];
map<int, int> mp;
struct node
{
int st, ed, id;
} cow[N];
bool cmp(node a, node b) { return a.st < b.st; }
int main()
{
int n,cnt=0;
scanf("%d", &n);
priority_queue<pii, vector<pii>, greater<pii> > que;
for (int i = 1; i <= n;i++)
{
scanf("%d%d", &cow[i].st, &cow[i].ed);
cow[i].id = i;
}
sort(cow + 1, cow + 1 + n,cmp);
for (int i = 1; i <= n; i++)
{
int st=cow[i].st, ed=cow[i].ed,id=cow[i].id;
if (i == 1)
{
mp[id] = 1;
cnt++;
que.push({ed, cnt});
continue;
}
auto t = que.top();
if (ed<=t.first||st<=t.first)
{
cnt++;
que.push({ed, cnt});
mp[id] = cnt;
}
else if(st>t.first)
{
//if(i==4)printf("%d %d\n", t.first, t.second);
que.pop();
que.push({ed, t.second});
//if(i==4)printf("%d %d\n", que.top().first, que.top().second);
mp[id] = t.second;
}
}
printf("%d\n", que.size());
for (int i = 1; i <= n;i++)
{
printf("%d\n", mp[i]);
}
return 0;
}
CFAB题特训
A - Bit++
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
int main()
{
int t;
int x = 0;
scanf("%d", &t);
while(t--)
{
char s[10];
scanf("%s", s);
int len = strlen(s);
if(s[0]=='+'||s[1]=='+')
x++;
else if(s[0]=='-'||s[1]=='-')
x--;
}
printf("%d\n", x);
return 0;
}
B - Gravity Flip
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
int a[N];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n ;i++)
scanf("%d", &a[i]);
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n;i++)
{
printf("%d ", a[i]);
}
return 0;
}
C - Blood Pressure
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
int main()
{
double a, b;
scanf("%lf%lf", &a, &b);
double c = (a - b) / 3 + b;
printf("%lf\n", c);
return 0;
}
D - Vasya and Books
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
using namespace std;
typedef long long ll;
const int N=2e5+10,INF=1e8;
int a[N],mp[N];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n ;i++)
{
scanf("%d", &a[i]);
mp[a[i]] = i;
}
int sum = 0,vis=0;
for (int i = 1; i <= n;i++)
{
int x;
scanf("%d", &x);
int xx = mp[x];
// printf("%d\n", xx);
if(xx<vis)
printf("0 ");
else
printf("%d ", xx-vis);
vis = max(vis, xx);
}
return 0;
}
E - Cycle Hit
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
int main()
{
int k[10] = {0};
char a[5];
for (int i = 1; i <= 4;i++)
{
cin >> a;
if(a[0]=='2')
k[2]=1;
else if(a[0]=='3')
k[3] = 1;
else if(a[0]=='H'&&a[1]=='R')
k[4] = 1;
else if(strlen(a)==1)
k[1] = 1;
}
int sum = 0;
for (int i = 1; i <= 4;i++)
sum += k[i];
if(sum==4)
printf("Yes\n");
else printf("No\n");
return 0;
}
F - Technogoblet of Fire
思路:一开始看好长一段像阅读理解。。。补题的时候看了一下,大概是这个要从很多个学校里选第一名,然后给你很多个学生的能力值和他的学校号码,然后给你k个人,你可以让选择某个人把他加到一个假的新的学校里去让他变成第一名,问要多少次操作才能让他们这k个人都被选上。对于这k个人我们判断一下他们是不是各自学校里的第一名如果是的话就不用操作,不是的话就加到一个新学校里去。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N=1e5+10,INF=1e8;
PII a[N];
int main()
{
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= n;i++)
{
scanf("%d", &a[i].first);
}
for (int i = 1; i <= n;i++)
{
scanf("%d", &a[i].second);
}
int cnt = 0;
for (int i = 1; i <= k;i++)
{
int x;
scanf("%d", &x);
for (int j = 1; j <= n;j++)
{
if(a[x].first<a[j].first&&a[x].second==a[j].second)
{
cnt++;
break;
}
}
}
printf("%d\n",cnt);
return 0;
}
G - Hate "A"
一开始是想把是a的个数记下来然后还原成两段,再切成两半去判断,但下去后想了做法还是有问题。看了下题解发现个很精简的代码,就是搞两个串一个读入当前串的字母,然后另外一个读入不是a的字母,即原字符串删去a后的字符串,一边读一边判断如果两个串的大小加起来等于原字符串的并且把它们接起来相等的话就说明成功的,否则失败。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
int main()
{
string a,b,c;
cin >> a;
for (int i = 0; i < a.size();i++)
{
b += a[i];
if(a[i]!='a')
c += a[i];
if(b.size()+c.size()==a.size()&&b+c==a)
{
cout << b;
return 0;
}
}
cout << ":(";
return 0;
}
H - chokudai
只差这个dp没补
I - The Miracle and the Sleeper
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
int a[N];
int main()
{
int t;
cin >> t;
while(t--)
{
int l, r;
cin >> l >> r;
if(l>(r/2))
printf("%d\n", r % l);
else
printf("%d\n",r % (r / 2+1));
}
return 0;
}
J - Scenes From a Memory
写的有点啰嗦,简而言之就是题目已经保证是一定有解的了,那我们只要找第一个不是素数的数输出就好,如果全是素数的话就判断一下它们拼成的两位数是不是素数就好
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N=1e5+10,INF=1e8;
PII a[N];
bool pm(int x)
{
if(x==1)
return 0;
if(x==2)
return 1;
for (int i = 2; i < x;i++)
{
if(x%i==0)
return 0;
}
return 1;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
char s[55] = {0};
int n,f=0;
scanf("%d", &n);
scanf("%s", s);
int m = strlen(s),ans=0;
for (int i = 0; i < m;i++)
{
if(!pm(s[i]-'0'))
{
f = 1;
ans = s[i]-'0';
break;
}
}
if(f)
{
printf("1\n");
printf("%d\n", ans);
continue;
}
else
{
for (int i = 0; i < m - 1;i++)
{
int ff = 0;
for (int j = i + 1; j < m;j++)
{
int a = (s[i] - '0') * 10 + (s[j] - '0');
if(!pm(a))
{
printf("2\n");
printf("%c%c\n", s[i], s[j]);
ff = 1;
break;
}
}
if(ff)
break;
}
}
}
return 0;
}
K - Weak Password
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
int a[N];
int main()
{
char s[10];
cin >> s;
int f = 1;
for (int i = 0; i < 3;i++)
{
if(s[i]!=s[i+1])
{
f = 0;
break;
}
}
if(f)
printf("Weak\n");
else
{
int f1 = 0;
for (int i = 0; i < 3;i++)
{
if(((s[i]-'0'+1)%10)!=(s[i+1]-'0'))
{
f1 = 1;
}
}
if(!f1)
printf("Weak\n");
else
printf("Strong\n");
}
return 0;
}
L - Road Optimization
dp,一开始尝试写了一下n2循环的dp[i][j],定义的是前i个点取j个路标的最小值,emmm后面也没推出来。后面去搜了题解看了一下发现是n3的dp[i][j],定义的是从前i个路标保留j个路标的最小值。就有转移方程dp[i][x] = min(dp[i][x], dp[j][x - 1] + b[j] * (a[i] - a[j]))
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N=500+10,INF=1e9+10;
int a[N], b[N];
int dp[N][N];
int main()
{
int n, l, k;
scanf("%d%d%d", &n, &l, &k);
for (int i = 0; i <= n + 1;i++)
for (int j = 0; j <= n + 1;j++)
dp[i][j] = INF;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (int i = 1; i <= n;i++)
scanf("%d", &b[i]);
a[n + 1] = l;
dp[1][0] = 0;
for (int i = 1; i <= n + 1; i++)
for (int j = 1; j < i; j++)
for (int x = 1; x <= j; x++)
dp[i][x] = min(dp[i][x], dp[j][x - 1] + b[j] * (a[i] - a[j]));
int ans = INF;
for (int i = 0; i <= k; i++)
{
ans = min(ans, dp[n + 1][n - i]);
}
printf("%d\n", ans);
return 0;
}