比赛入口
B 都说小镇的切糕贵
做法:题意是要求一个字符串中能包含该串出现所有字符的最小长度,是滑动窗口技巧题目的典型例题。
具体滑动窗口实现:
如果左边界遇到 与当前s[i]相同且s[i]之前出现过 的,更新最小窗口长度res = min(res, i-l+1);
如果左边界遇到 与当前s[i]相同且s[i]之前未出现过 的,把flag置为1,处理完l后强制更新res = i - l + 1。
代码:
#include<bits/stdc++.h>
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
typedef long long LL;
typedef pair<LL, int> pli;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
typedef map<char, int> mci;
typedef map<string, int> msi;
template<class T>
void read(T &res) {
int f = 1; res = 0;
char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); }
res *= f;
}
int main() {
fio
int n; cin >> n;
string s; cin >> s;
int len = s.size();
unordered_map<char, int> vis;
vis.clear();
int l = 0, res = n;
for(int i = 0; i < len; ++i) {
int flag = 0;
if(!vis[s[i]]) flag = 1; //与当前s[i]相同且s[i]之前未出现过
vis[s[i]]++;
while(vis[s[l]] > 1) {
vis[s[l]]--; l++;
}
if(flag) res = i - l + 1;
else res = min(res, i - l + 1);
}
printf("%d\n", res);
return 0;
}
C Guard the empire
做法:题意是要求最少需要多少个半径为r的圆(圆心都在x坐标上),能覆盖住n个给定坐标的点。做法就是找到每个圆能够取到的圆心位置的区间,再贪心找最少有多少个点能将所有区间都部分覆盖。
代码:
#include<bits/stdc++.h>
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
typedef long long LL;
typedef pair<LL, int> pli;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
typedef pair<double, double> pdd;
typedef map<char, int> mci;
typedef map<string, int> msi;
template<class T>
void read(T &res) {
int f = 1; res = 0;
char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); }
res *= f;
}
const int N = 1003;
struct Qujian {
int l, r;
bool operator < (const Qujian &c) const {
if(l != c.l) return l < c.l;
return r < c.r;
}
}qj[N];
double toQj(double y, double r) {
return sqrt(r*r - y*y);
}
int main() {
int n, c = 0;
double r;
while(scanf("%d%lf", &n, &r), n || r) {
double x, y, cha;
int flag = 1;
for(int i = 0; i < n; ++i) {
scanf("%lf%lf", &x, &y);
if(y > r) { //如果超出了圆能表示的范围
flag = 0; continue;
}
cha = toQj(y, r);
//找出可以覆盖住该点的圆心范围
qj[i].l = x - cha; qj[i].r = x + cha;
}
if(!flag) {
printf("Case %d: %d\n", ++c, -1);
} else {
//贪心找能覆盖住所有点的圆个数
sort(qj, qj+n);
double mid = qj[0].r;
int res = 1;
for(int i = 1; i < n; ++i) {
if(qj[i].l > mid) {
mid = qj[i].r; res++;
} else if (qj[i].r < mid) {
mid = qj[i].r;
}
}
printf("Case %d: %d\n", ++c, res);
}
}
return 0;
}
H Magic necklace
做法:有一串标号1~n的数字,要求每个数字不相邻的方案数,因为数字很小,直接上暴力预处理即可,这里用了next_permutation函数。
代码:
#include<bits/stdc++.h>
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
typedef long long LL;
typedef pair<LL, int> pli;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
typedef map<char, int> mci;
typedef map<string, int> msi;
template<class T>
void read(T &res) {
int f = 1; res = 0;
char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); }
res *= f;
}
int res[15], mid[15], n;
int check(int n) {
for(int i = 1; i <= n; ++i) {
if(fabs(mid[i+1] - mid[i]) == 1) return 0;
}
return 1;
}
int main() {
//暴力预处理
res[1] = 1; res[2] = 0;
for(int i = 3; i <= 11; ++i) {
for(int j = 1; j <= i; ++j) mid[j] = j;
mid[i+1] = mid[1];
while(next_permutation(mid+2, mid+i+1)) {
if(check(i)) res[i]++;
}
res[i] = res[i] / 2;
}
int t; scanf("%d", &t);
while(t--) {
scanf("%d", &n);
printf("%d\n", res[n]);
}
return 0;
}
I 恋爱之子
做法:一些线段,所有相交的线段成为一个集合,要求有多少个集合,几何板子——判断两条线段是否相交,再用并查集即可。
代码:
#include<bits/stdc++.h>
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
typedef long long LL;
typedef pair<LL, int> pli;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
typedef map<char, int> mci;
typedef map<string, int> msi;
template<class T>
void read(T &res) {
int f = 1; res = 0;
char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); }
res *= f;
}
const int N = 4003;
struct Dian { //点
LL x, y;
};
struct Xian { //线
Dian s, e;
} xian[N];
double mul(Dian a, Dian b, Dian c) { //向量叉积
return ((double)(b.x-a.x) * (c.y-a.y) - (double)(c.x-a.x) * (b.y-a.y));
}
int isJiao(Xian a, Xian b) { //判断两个线段是否相交
if( min(a.s.x, a.e.x) <= max(b.s.x, b.e.x) &&
max(a.s.x, a.e.x) >= min(b.s.x, b.e.x) &&
min(a.s.y, a.e.y) <= max(b.s.y, b.e.y) &&
max(a.s.y, a.e.y) >= min(b.s.y, b.e.y) &&
mul(b.s, a.s, b.e) * mul(b.s, b.e, a.e) >= 0 &&
mul(a.s, b.s, a.e) * mul(a.s, a.e, b.e) >= 0 )
return 1;
return 0;
}
int a[N];
int Find(int x) {
return a[x] == x ? x : a[x] = Find(a[x]);
}
void Merge(int x, int y) {
int fx = Find(x), fy = Find(y);
if(fx != fy) {
a[fy] = fx;
}
}
int main() {
int n; scanf("%d", &n);
for(int i = 0; i < n; ++i) {
scanf("%lld%lld%lld%lld", &xian[i].s.x, &xian[i].s.y, &xian[i].e.x, &xian[i].e.y);
a[i] = i;
}
for(int i = 0; i < n - 1; ++i) {
for(int j = i + 1; j < n; ++j) {
if(isJiao(xian[i], xian[j])) Merge(i, j);
}
}
int res = 0;
for(int i = 0; i < n; ++i) {
if(a[i] == i) res++;
}
printf("%d\n", res);
return 0;
}
K 集训队的依赖关系
做法:由一棵无根树建立超级源点0,再对这棵树进行树形dp,现在还是对树形dp很迷,等彻底学会再来写一篇树形dp专题,咕咕咕~
代码:
#include<bits/stdc++.h>
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
typedef long long LL;
typedef pair<LL, int> pli;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
typedef map<char, int> mci;
typedef map<string, int> msi;
template<class T>
void read(T &res) {
int f = 1; res = 0;
char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); }
res *= f;
}
const int N = 5100;
int n, m, S;
int val[N], in[N], cost[N];
LL now[N], dp[N][N];
vector<int> ve[N];
void dfs(int u) {
for(int i = S; i >= 0; --i) {
if(i >= cost[u]) dp[u][i] = dp[u][i-cost[u]] + val[u];
else dp[u][i] = -1e18;
}
for(int v : ve[u]) {
for(int i = 0; i <= S; ++i) dp[v][i] = dp[u][i];
dfs(v);
for(int i = 0; i <= S; ++i) dp[u][i] = max(dp[u][i], dp[v][i]);
}
}
int main() {
int u, v;
read(n); read(S); read(m);
for(int i = 1; i <= n; ++i) read(val[i]);
for(int i = 1; i <= n; ++i) read(cost[i]);
for(int i = 0; i < m; ++i) {
read(u); read(v);
ve[v].push_back(u);
in[u]++;
}
for(int i = 1; i <= n; ++i) {
if(in[i]) continue;
ve[0].push_back(i);
}
dfs(0);
LL ans = 0;
for(int i = 0; i <= S; ++i) ans = max(ans, dp[0][i]);
printf("%lld\n", ans);
return 0;
}
L 金牌厨师HiLin与HJGG
做法:题意就是有一个大矩阵,要求选一个最小的m,使得m*m矩阵中所有数字之和大于等于k,用到二位前缀和。
代码:
#include<bits/stdc++.h>
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
typedef long long LL;
typedef pair<LL, int> pli;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
typedef map<char, int> mci;
typedef map<string, int> msi;
template<class T>
void read(T &res) {
int f = 1; res = 0;
char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); }
res *= f;
}
const int N = 2003;
int a[N][N];
LL sum[N][N];
int n, k;
int solve(int x, int y, int nn) {
x--; y--;
if(sum[x+nn][y+nn] - sum[x][y+nn] - sum[x+nn][y] + sum[x][y] >= k) return 1;
return 0;
}
int main() {
read(n); read(k);
//二维前缀和预处理
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= n; ++j) {
read(a[i][j]);
sum[i][j] = a[i][j] + sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1];
}
}
//暴力枚举每个m,加剪枝
LL ans = n;
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= n; ++j) {
for(int nn = ans; nn >= 1 && nn <= n-i+1 && nn <= n-j+1; --nn) {
if(solve(i, j, nn)) {
ans = nn;
} else break;
}
}
}
if(sum[n][n] < k) puts("I'm a Gold Chef!");
else printf("%lld\n", ans);
return 0;
}