Problem C. Cyber Language
题意:
给定小写的中文拼音,输出大写的首字母。
题解:
将第一个字母和空格后的字母大写输出就可以了。
代码:
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
string s;
void solve(){
getline(cin, s);
cout << (char)(s[0] - 'a' + 'A');
int len = s.length();
bool flag = false;
for(int i = 0; i < len; i++){
if(s[i] == ' '){
flag = true;
continue;
}
if(flag){
cout << (char)(s[i] - 'a' + 'A');
flag = false;
}
}
cout << '\n';
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t = 1;
cin >> t;
getline(cin, s);
while(t--){
solve();
}
}
Problem I. Package Delivery
题意;
有n个快递,给定到货日期和截止日期,每次最多拿k个,问最少需要几次拿完。
题解:
贪心。
首先按到货日期为第一要素排序,然后按照截止日期从早到晚依次看。我们设置一个缓冲地带,对于一个截止日期,我们首先看缓冲地带是否有截止日期早于当先考虑的截止日期,有则拿;然后考虑快递站里的包裹是否有到货日期早于该截止日期的,有则考虑它的截止日期,如果早于当前考虑的截止日期,则拿,否则放入缓冲区;最后再检查缓冲区,因为要尽可能拿满k个。
代码:
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, k;
struct pak{
int l, r;
bool operator < (const pak a) const{
return r > a.r;
}
}p[N];
bool cmp(pak a, pak b){
return a.l < b.l;
}
void solve(){
cin >> n >> k;
set<int> s;
for(int i = 1; i <= n; i++){
cin >> p[i].l >> p[i].r;
s.insert(p[i].r);
}
sort(p + 1, p + 1 + n, cmp);
priority_queue<pak> q;
int cnt = 0, ans = 0, now = 1;
auto i = s.begin();
while(i != s.end()){
int it = *i;
while(!q.empty() && q.top().r <= it && cnt < k){
q.pop();
cnt ++;
}
while(now <= n && p[now].l < it && cnt < k){
if(p[now].r <= it) cnt ++;
else q.push(p[now]);
now ++;
}
while(cnt && !q.empty() && cnt < k){
cnt ++;
q.pop();
}
if(cnt) cnt = 0, ans ++;
else i ++;
}
cout << ans << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t = 1;
cin >> t;
while(t--){
solve();
}
}
Problem K. Taxi
题意:
二维坐标n个点,每个点有一个费用w,q次查询,每次给定一个坐标,社曼哈顿距离为d,问给定点到n个点中min(d, w)的最大值。
题解:
如果没有 w 的限制,那么是经典问题。根据 |x| = max(x, −x),有
max {|x′ − xi| + |y′ − yi|}
= max {max(x′ − xi, −x′ + xi) + max(y′ − yi, −y′ + yi)}
= max {x′ − xi + y′ − yi, −x′ + xi + y′ − yi, x′ − xi − y′ + yi, −x′ + xi − y′ + yi}
= max {(x′ + y′) + (−xi − yi), (x′ − y′) + (−xi + yi), (−x′ + y′) + (xi − yi), (−x′ − y′) + (xi + yi)}
分别记录 xi + yi、xi − yi、−xi + yi、−xi − yi 的最大值即可在 O(1) 时间内求出所有点到
(x′, y′) 的曼哈顿距离的最大值。
现在考虑加入 w 的限制。将所有城镇按照 w 从小到大排序,并记录排序后每个后缀的
xi + yi、xi − yi、−xi + yi、−xi − yi 的最大值,用于 O(1) 求给定点 (x′, y′) 到该后缀中所有点
的距离最大值。
选取按 w 排序后的第 k 个城镇,O(1) 求出给定点 (x′, y′) 到第 k..n 个城镇的距离最大值
d,有两种情况:
(1) < d,那么第 k..n 个城镇对答案的贡献至少为 。用 更新答案后,由于第 1..k 个
城镇的 w 值均不超过 ,因此它们不可能接着更新答案,考虑范围缩小至 [k + 1, n]。
(2) ≥ d,那么第 k..n 个城镇对答案的贡献为 d。用 d 更新答案后,考虑范围缩小至
[1, k − 1]。
最后二分答案即可。
代码:
#include<iostream>
#include<algorithm>
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N = 1e5 + 10;
int n, q, x, y, a[N], b[N], c[N], d[N];
struct node{
int x, y, w;
}p[N];
bool cmp(node a, node b){
return a.w < b.w;
}
void solve(){
cin >> n >> q;
for(int i = 1; i <= n; i++){
cin >> p[i].x >> p[i].y >> p[i].w;
}
sort(p + 1, p + 1 + n, cmp);
a[n + 1] = b[n + 1] = c[n + 1] = d[n + 1] = - INF;
for(int i = n; i; i --){
a[i] = max(a[i + 1], p[i].x + p[i].y);
b[i] = max(b[i + 1], p[i].x - p[i].y);
c[i] = max(c[i + 1], - p[i].x + p[i].y);
d[i] = max(d[i + 1], - p[i].x - p[i].y);
}
while(q--){
cin >> x >> y;
int l = 1, r = n, ans = 0, tmp;
while(l <= r){
int mid = (l + r) >> 1;
tmp = max(max(- x - y + a[mid], - x + y + b[mid]), max(x - y + c[mid], x + y + d[mid]));
if(p[mid].w >= tmp){
ans = max(ans, tmp);
r = mid - 1;
}
else {
ans = max(ans, p[mid].w);
l = mid + 1;
}
}
cout << ans << '\n';
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t = 1;
cin >> t;
while(t--){
solve();
}
}
Problem L. Two Permutations
题意:
给定长度为n的两个排列P和Q,一个长度为2n的数列S。每次从P或Q的最左侧去一个数字,加在R的末尾,问使R = S的取法有几种。
题解:
首先特判序列 S 中每个数字出现次数不都为 2 的情况,此时答案为 0。
动态规划,设 f (i,j) 表示 P 的前 i 项匹配上了 S,且 Pi 匹配 S 中数字 Pi 第 j 次出现的位
置时,有多少种合法的方案。由于 S 中每个数字出现次数都为 2,因此状态数为 O(n)。转移时
枚举 Pi+1 匹配哪个位置,那么 Pi 匹配的位置与 Pi+1 匹配的位置中间的那段连续子串需要完
全匹配 Q 中对应的子串,使用字符串 Hash 进行 O(1) 判断即可。
代码:
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<set>
#include<queue>
using namespace std;
#define debug(x) cout<<#x<<": "<<(x)<<endl
#define debug2(x,y) cout<<#x<<endl;for(int i=1;i<=y;++i)cout<<x[i]<<": ";cout<<endl
#define mem(x,y) memset(x,y,sizeof(x));
#define int long long
#define ll unsigned long long
#define double long double
const int inf=0x3f3f3f3f3f3f3f3f;
const double eps=1e-9;
const double PI=acos(-1.0);
const int b=131;
const int maxn=5e6+10;
ll bb[maxn];
const int mod=998244353;
int n;
int a[maxn],c[maxn];
int s[maxn];
int pos[maxn][3];
ll has1[maxn],has2[maxn];
int dp[maxn][3];
void init()
{
bb[0]=1;
for(int i=1;i<maxn;++i)
bb[i]=bb[i-1]*b;
}
void hashhash()
{
for(int i=1;i<=n;++i)
has1[i]=has1[i-1]*b+c[i]*b;
for(int i=1;i<=2*n;++i)
has2[i]=has2[i-1]*b+s[i]*b;
}
ll get1(int l,int r)
{
return has1[r]-has1[l-1]*bb[r-l+1];
}
ll get2(int l,int r)
{
return has2[r]-has2[l-1]*bb[r-l+1];
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// cout<<fixed<<setprecision(6);
init();
int _;
cin>>_;
while(_--)
{
cin>>n;
for(int i=1;i<=n;++i)
pos[i][1]=pos[i][2]=dp[i][1]=dp[i][2]=0;
for(int i=1;i<=n;++i)
cin>>a[i];
for(int i=1;i<=n;++i)
cin>>c[i];
for(int i=1;i<=2*n;++i)
{
cin>>s[i];
if(pos[s[i]][1])pos[s[i]][2]=i;
else pos[s[i]][1]=i;
}
hashhash();
if(get1(1,pos[a[1]][1]-1)==get2(1,pos[a[1]][1]-1))dp[1][1]=1;
if(get1(1,pos[a[1]][2]-1)==get2(1,pos[a[1]][2]-1))dp[1][2]=1;
for(int i=1;i<n;++i)
{
for(int j=1;j<=2;++j)
{
if(dp[i][j]==0)continue;
int l=pos[a[i]][j],r=pos[a[i+1]][1];
if(l<r&&get1(l-i+1,r-i-1)==get2(l+1,r-1))dp[i+1][1]=(dp[i+1][1]+dp[i][j])%mod;
r=pos[a[i+1]][2];
if(l<r&&get1(l-i+1,r-i-1)==get2(l+1,r-1))dp[i+1][2]=(dp[i+1][2]+dp[i][j])%mod;
}
}
// for(int i=1;i<=n;++i)
// cout<<dp[i][1]<<" ";cout<<'\n';
// for(int i=1;i<=n;++i)
// cout<<dp[i][2]<<" ";cout<<'\n';
int ans=0;
if(get1(pos[a[n]][1]-n+1,n)==get2(pos[a[n]][1]+1,2*n))ans=(ans+dp[n][1])%mod;
if(get1(pos[a[n]][2]-n+1,n)==get2(pos[a[n]][2]+1,2*n))ans=(ans+dp[n][2])%mod;
cout<<ans<<'\n';
}
return 0;
}
Problem B. Boss Rush
题意:
n个技能,打一个H血量的BOSS,每个技能有一个演出时间和持续时间,在演出时间里不能发动其他技能,给定持续效果每秒的伤害值。问最短时间干掉BOSS。
题解:
二分答案,转化为判断 T 帧内能否打败 BOSS,即求出 T 帧内能打出的最高伤害,判断是
否大于等于 H。
从前往后依次发动若干个技能,则下一个技能可以发动的时刻等于之前发动过的技能的演
出时间之和,因此只和之前发动过哪些技能有关。设 fS 表示发动了 S 集合的技能,在 T 帧内
最多能结算多少伤害,枚举不在 S 中的某个技能 x 作为下一个技能进行转移,由于技能发动时
刻已知,因此可以 O(1) 计算出在 T 帧内下一个技能可以结算多少伤害。
代码:
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int N = 1e5 + 10;
const int M = 18;
int n, t[M], d[M], ans, sum[(1 << M) + 1];
int hp, f[(1 << M) + 1], dmg[M][N];
bool check(int x){
for(int i = 0; i < 1 << n; i++) f[i] = -1;
f[0] = 0;
for(int i = 0; i < 1 << n; i++){
int tmp = f[i];
if(tmp < 0) continue;
if(tmp >= hp) return true;
int tmpp = sum[i];
if(tmpp > x) continue;
for(int j = 0; j < n; j++){
if(!(i >> j & 1)){
if(tmpp + d[j] - 1 <= x) f[i | (1 << j)] = max(f[i | (1 << j)], tmp + dmg[j][d[j] - 1]);
else f[i | (1 << j)] = max(f[i | (1 << j)], tmp + dmg[j][x - tmpp]);
}
}
}
return false;
}
void solve(){
cin >> n >> hp;
ans = -1;
int l = 0, r = 0;
for(int i = 0; i < n; i++){
cin >> t[i] >> d[i];
r += t[i] + d[i] - 1;
for(int j = 0; j < d[i]; j++) cin >> dmg[i][j];
for(int j = 1; j < d[i]; j++) dmg[i][j] += dmg[i][j - 1];
}
for(int i = 1; i < 1 << n; i++){
sum[i] = sum[i - (i & -i)] + t[__builtin_ctz(i & -i)];
}
while(l <= r){
int mid = (l + r) >> 1;
if(check(mid)){
ans = mid;
r = mid - 1;
}
else l = mid + 1;
}
cout << ans << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t = 1;
cin >> t;
while(t--){
solve();
}
}
Problem H. Laser Alarm
题意:
就是问给定空间内若干条线段,插入一个平面最多能和几条线段有交点。
题解:
三个不共线的点可以确定一个平面。对于任意一个平面,将其调整至经过三个顶点,结果
不会变差。因此枚举三个顶点得到平面,然后 O(n) 计算触碰了该平面的线段数,更新答案即
可。所有点都共线的情况需要特判。
代码:
#include<iostream>
#include<algorithm>
#include<cmath>
#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const double eps = 1e-9;
const int N = 510;
struct dot{
double x, y, z;
dot(double xx = 0, double yy = 0, double zz =0) : x(xx), y(yy), z(zz){}
};
inline void scan(dot &a){cin >> a.x >> a.y >> a.z;}
inline void print(dot &a){cout << a.x << a.y << a.z;}
struct line{
dot d, a, b;
line(dot dd, dot aa, dot bb) : d(dd), a(aa), b(bb){}
};
struct plane{
dot d, a;
plane(dot dd, dot aa) : d(dd), a(aa){}
};
inline dot operator + (const dot &a, const dot &b){return dot(a.x + b.x, a.y + b.y, a.z + b.z);}
inline dot operator - (const dot &a, const dot &b){return dot(a.x - b.x, a.y - b.y, a.z - b.z);}
inline double operator * (const dot &a, const dot &b){return a.x * b.x + a.y * b.y + a.z * b.z;}
inline dot operator ^ (const dot &a, const dot &b)
{
return dot(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
}
inline double len(const dot &a){return sqrt(a.x * a.x + a.y * a.y + a.z * a.z);}
int n;
dot a[N];
inline bool inte(const plane &p, const line &l){
double t1 = (l.a - p.a) * p.d, t2 = (l.b - p.a) * p.d;
if(abs(t1) < eps || abs(t2) < eps) return true;
if((t1 > eps) ^ (t2 > eps)) return true;
return false;
}
inline bool coll(const dot &a, const dot &b){
return abs(len(a ^ b)) < eps;
}
void solve(){
cin >> n;
for(int i = 1; i <= 2 * n; i ++) scan(a[i]);
int ans = 0;
for(int i = 1; i < 2 * n; i ++){
for(int j = 1; j < i; j ++){
for(int k = 1; k < j; k++){
dot t = (a[i] - a[j]) ^ (a[i] - a[k]);
if(len(t) < eps) continue;
plane p = plane(t, a[i]);
int res = 0;
for(int c = 1; c <= n; c++){
line l = line(a[2 * c - 1] - a[2 * c], a[2 * c - 1], a[2 * c]);
if(inte(p, l)) res ++;
}
ans = max(ans, res);
}
int res = 0;
for(int c = 1; c <= n; c++){
dot t = a[i] - a[j];
if(coll(t, a[i] - a[2 * c - 1]) || coll(t, a[i] - a[2 * c])) res ++;
}
ans = max(ans, res);
}
}
cout << ans << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t = 1;
cin >> t;
while(t--){
solve();
}
}