还是没有更新rating,期末考试成绩也没出来,有点烦
今天休假,踢了场球,权当放松一下
7.26场地址
E题:那道出了好多次的题,还是贴一下吧hhh
//HDU5649的板子
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <Stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout << "[" << x << "]"
#define FIN freopen("input.txt", "r", stdin)
#define FOUT freopen("output.txt", "w+", stdout)
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MX = 1e5 + 5;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
int n, pos, Q;
int op[MX][3];
int S[MX << 2], col[MX << 2], A[MX];
void push_up(int rt) {
S[rt] = S[rt << 1] + S[rt << 1 | 1];
}
void push_down(int rt, int m, int len) {
if (col[rt] != -1) {
int szr = len >> 1, szl = len - szr;
col[rt << 1] = col[rt << 1 | 1] = col[rt];
S[rt << 1] = szl * col[rt]; S[rt << 1 | 1] = szr * col[rt];
col[rt] = -1;
}
}
void build(int x, int l, int r, int rt) {
col[rt] = -1;
if (l == r) {
S[rt] = A[l] <= x ? 0 : 1;
return;
}
int m = (l + r) >> 1;
build(x, lson); build(x, rson);
push_up(rt);
}
int query(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) {
return S[rt];
}
int ret = 0, m = (l + r) >> 1;
push_down(rt, m, r - l + 1);
if (L <= m) ret += query(L, R, lson);
if (R > m) ret += query(L, R, rson);
return ret;
}
void update(int L, int R, int x, int l, int r, int rt) {
if (L > R) return;
if (L <= l && r <= R) {
col[rt] = x;
S[rt] = (r - l + 1) * x;
return;
}
int m = (l + r) >> 1;
push_down(rt, m, r - l + 1);
if (L <= m) update(L, R, x, lson);
if (R > m) update(L, R, x, rson);
push_up(rt);
}
void print(int l, int r, int rt) {
if (l == r) {
fuck(S[rt]);
return;
}
int m = (l + r) >> 1;
push_down(rt, m, r - l + 1);
print(lson); print(rson);
}
bool check(int x) {
build(x, 1, n, 1);
for (int i = 1; i <= Q; i++) {
int sum = query(op[i][1], op[i][2], 1, n, 1);
if (op[i][0] == 0) {
update(op[i][1], op[i][2] - sum, 0, 1, n, 1);
update(op[i][2] - sum + 1, op[i][2], 1, 1, n, 1);
}
else {
update(op[i][1], op[i][1] + sum - 1, 1, 1, n, 1);
update(op[i][1] + sum, op[i][2], 0, 1, n, 1);
}
}
int w = query(pos, pos, 1, n, 1);
return w == 0;
}
int main() {
scanf("%d%d", &n, &Q);
for (int i = 1; i <= n; i++) {
scanf("%d", &A[i]);
}
int a, b, c;
for (int i = 1; i <= Q; i++) {
scanf("%d%d", &a, &b);//上升为l<r,0开头
if (a < b) { op[i][0] = 0; op[i][1] = a; op[i][2] = b; }
else { swap(a, b); op[i][0] = 1; op[i][1] = a; op[i][2] = b; }
}
pos = (n + 1) / 2;
int l = 1, r = n, m;
while (l <= r) {
m = (l + r) >> 1;
if (check(m)) r = m - 1;
else l = m + 1;
}
printf("%d\n", r + 1);
return 0;
}
I题:
题意:有n堆纸牌,其中n为偶数,每次操作可以拿走一堆纸牌上的一张或者是添加一张,问最少需要多少次可以使得所有纸牌堆中平方数的数量和非平方数的数量一样
解法:set维护即可,哪种多我们变那种,注意set中添加的元素的值
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<set>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
ll a[maxn];
multiset<ll>s1, s2;//set1是非平方数,set2是平方数
int maxx = 1e9 + 2;
int maxxx = 1e9 + 1;
int main() {
int n; scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%lld", &a[i]);
ll tmp = 1ll * sqrt(a[i]);
if (tmp*tmp == a[i]) {//判断一个数是不是平方数
if (a[i] == 0)s2.insert(2);
else s2.insert(1);
}
else{
s1.insert(min(a[i] - tmp*tmp, (tmp + 1)*(tmp + 1) - a[i]));
}
}
ll ans = 0;
while (s2.size() > s1.size()) {//平方数多
ans += *s2.begin();
s2.erase(s2.begin());
s1.insert(maxxx);
}
while (s1.size() > s2.size()) {//非平方数比平方数多
ans += *s1.begin();
s1.erase(s1.begin());
s2.insert(maxx);
}
printf("%lld\n", ans);
return 0;
}
J题:
题意:会给出你一堆信息,假设有n个人,每个人会有一堆电话号码,其中如果一个电话号码是另一电话号码的后缀就不要这个电话号码,整理统计之后输出所有人的信息
解法:暴力枚举后缀的所有可能情况,注意数据结构的使用
#include<cstdio>
#include<iostream>
#include<map>
#include<string>
#include<set>
using namespace std;
typedef map<string, set<string>>::iterator mpi;
typedef set<string>::iterator si;
map<string, set<string>>mp;
int main() {
int n; scanf("%d", &n);
string s, ss;
for (int i = 0; i < n; i++) {
cin >> s; int m; scanf("%d", &m);
for (int j = 0; j < m; j++) {
cin >> ss;
mp[s].insert(ss);
}
}
for (mpi it = mp.begin(); it != mp.end(); it++) {
set<string> &temp = it->second;
for(si itt=temp.begin();itt!=temp.end();itt++){
for (int k = itt->size() - 1; k >= 1; k--) {
string s = string(itt->begin() + k, itt->end());
si ns = temp.find(s);
if (ns!=temp.end())temp.erase(s);
}
}
}
cout << mp.size() << endl;
for (mpi it = mp.begin(); it != mp.end(); it++) {
cout << it->first << " " << it->second.size() << " ";
set<string> &temp = it->second;
for (si itt = temp.begin(); itt != temp.end(); itt++) {
cout << *itt << " ";
}
cout << endl;
}
return 0;
}
A题:
题意:有一个字符串,你看过这个字符串一次,然后我们从这个字符串的某个节点反转这段字符串,告诉你这个字符串的首字母,然后你可以再随便问一个字母,问你最后能猜到从哪个位置反转的几率为多少
解法:见代码注释
/*
设串首的字符为c1,串中的打开的字符为c2,他们两个之间的距离为d,
如果字符对(c1,c2)只在d这个距离出现过一次,那么就可以唯一的确定那个反转的k值。
我们处理出字符串中任意两个字符之间的距离,然后再枚举起始字符,
看所有距离里面中那一边的所有字符,他们的配对情况是不是只有一种,
是的话就累加更新ans
要注意的是,对于一个确定的c1,它的最优情况只有一种或者说,
我们只能期盼于那种最好的情况,而下面max的原因就是这个
对于每个C1,它的概率是Σ(P/cnt(C1))=(ΣP)/n
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 5000 + 5;
char str[maxn * 2];
int a[26][26][maxn];
int tot, n;
int main() {
scanf("%s", str + 1);
n = strlen(str + 1);
for (int i = 1; i <= n; i++)
str[i + n] = str[i];
//这里可以定义一个string,然后直接s+=s即可
memset(a, 0, sizeof(a));
for (int i = 1; i <= n; i++)
for (int j = i + 1; j < i + n; j++)
a[str[i] - 'a'][str[j] - 'a'][j - i + 1]++;
tot = 0;
for (int i = 0; i < 26; i++) {
int tmp = 0;
for (int j = 1; j <= n; j++) {
int sum = 0;
for (int k = 0; k < 26; k++)
if (a[i][k][j] == 1) sum++;
tmp = max(sum, tmp);
}
tot += tmp;
}
printf("%.14f\n", double(tot) / n);
return 0;
}
G题:
题意:有一个序列,每次删除最长的连续序列,问多少次操作之后可以将这个序列删除完
解法:两个集合,维护序列中的信息,注意序列对pair排序是先排first
/*
这段代码写的非常好,可以多借鉴一下
*/
#include<cstdio>
#include<iostream>
#include<map>
#include<set>
using namespace std;
const int maxn = 2e5 + 6;
int n;
int a[maxn];
set<pair<int, int> >s1, s2;
int main(){
cin >> n;
int l, r;
for (l = 1, r = 1; r <= n; r++) {
cin >> a[r];
if (a[r] != a[l]) {
s1.insert({ l - r, l });//长度,起始点
s2.insert({ l, r - l });//起始点,长度
l = r;
}
}
s1.insert({ l - r, l });
s2.insert({ l, r - l });
int cnt = 0;
while (s1.size() > 1) {
cnt++;
pair<int, int> temp = *s1.begin();//取最长的区间
s1.erase(temp);
int st = temp.second;//st为起点
int len = -temp.first;//len为长度
auto lpos = s2.lower_bound({ st, len });//找到这个区间左边的那个区间
auto rpos = s2.upper_bound({ st, len });//找到这个区间右边的那个区间
if (lpos == s2.begin() || rpos == s2.end()) {//没有的话就跳过,直接删除
s2.erase({ st, len });
continue;
}
lpos--;//lower_bound的原因,左移
if (a[lpos->first] == a[rpos->first]) {
// ↑ ↑ 验证两端区间一样不一样,即验证他们的开始字符一样不一样
//一样的话就删除这两段区间,并更新维护新的那一段
int st1 = lpos->first, st2 = rpos->first;
int len1 = lpos->second, len2 = rpos->second;
s1.erase({ -len1, st1 });
s1.erase({ -len2, st2 });
s2.erase({ st1, len1 }), s2.erase({ st2, len2 });
s1.insert({ -len1 - len2, st1 });
s2.insert({ st1, len1 + len2 });
}
s2.erase({ st, len });
}
cnt++;
cout << cnt << endl;
return 0;
}
Uva1619
题意:给出一个长度为n的正整数序列ai,求出一段连续子序列
al~ar使得(al+…+ar)*min(al+…+ar)尽可能大,如果有相同的结果,输出区间长度最小的那个,如果再有相同的,输出最左边的那个
解法:见代码注释,这道题主要是RMQ+迭代搜索,思路也挺好的
/*
基本思路,通过RMQ实现将(n^2)降到(nlogn)
我们查找到最小值的下标时,接下来需要递归的遍历的他的左区间和右区间就行了
因为如果还继续遍历包含当前最小值的区间的话,由于长度缩短,要求的结果必定会变小,没必要了
*/
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const int maxm = 30;
const int inf = 0x3fffffff;
int a[maxn], L , R , n;
ll sum[maxn], ans=-inf;
/*-----------------------------------------------------------------------------------------------*/
/*----------------------------------------RMQ模板-------------------------------------------*/
int d_min[maxn][maxm], d_max[maxn][maxm];//值
int minpos[maxn][maxm], maxpos[maxn][maxm];//下标
//预处理区间最大最小值
void RMQ_init(int n){
int i, j;
for (i = 1; i <= n; i++){
d_min[i][0] = a[i];
d_max[i][0] = a[i];
}
for (j = 1; (1 << j) <= n; j++)
for (i = 1; i + j - 1 <= n; i++){
d_min[i][j] = min(d_min[i][j - 1], d_min[i + (1 << (j - 1))][j - 1]);
d_max[i][j] = max(d_max[i][j - 1], d_max[i + (1 << (j - 1))][j - 1]);
}
}
//查询区间最小值
int RMQ_min(int l, int r){
int k = 0;
while ((1 << (k + 1)) <= r - l + 1) k++;
return min(d_min[l][k], d_min[r - (1 << k) + 1][k]);
}
//查询区间最大值
int RMQ_max(int l, int r){
int k = 0;
while ((1 << (k + 1)) <= r - l + 1) k++;
return max(d_max[l][k], d_max[r - (1 << k) + 1][k]);
}
//预处理区间最大最小值的下标
void RMQ_pos_init(int n, int b[]){
int i, j;
for (i = 1; i <= n; i++) {
maxpos[i][0] = i;
minpos[i][0] = i;
}
for (j = 1; (1 << j) <= n; j++)
for (i = 1; i + (1 << j) - 1 <= n; i++){
minpos[i][j] = b[minpos[i][j - 1]] < b[minpos[i + (1 << (j - 1))][j - 1]] ? minpos[i][j - 1] : minpos[i + (1 << (j - 1))][j - 1];
maxpos[i][j] = b[maxpos[i][j - 1]] > b[maxpos[i + (1 << (j - 1))][j - 1]] ? maxpos[i][j - 1] : maxpos[i + (1 << (j - 1))][j - 1];
}
}
//查询区间最大值的下标
int RMQ_pos_max(int s, int v, int b[]){
int k = (int)(log((v - s + 1)*1.0) / log(2.0));
return b[maxpos[s][k]] > b[maxpos[v - (1 << k) + 1][k]] ? maxpos[s][k] : maxpos[v - (1 << k) + 1][k];
}
//查询区间最小值的下标
int RMQ_pos_min(int s, int v, int b[]){
int k = (int)(log((v - s + 1)*1.0) / log(2.0));
return b[minpos[s][k]] < b[minpos[v - (1 << k) + 1][k]] ? minpos[s][k] : minpos[v - (1 << k) + 1][k];
}
/*----------------------------------------------------------------*/
/*----------------------------------------------------------------*/
void solve(int l, int r) {
if (r < l||l<1||r>n)return;
int pos = RMQ_pos_min(l, r, a);
ll tot = 1ll* a[pos] * (sum[r] - sum[l - 1]);
if (tot > ans) { L = l; R = r; ans = tot; }
else if (tot == ans) {
if ((R - L) > (r - l) || ((R - L) == (r - l) && l < L)) {
L = l; R = r;
}
}
solve(l, pos - 1);
solve(pos + 1, r);
}
int main() {
bool qwe = false;
while (scanf("%d", &n) != EOF) {
sum[0] = 0; ans = -1;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
sum[i] = sum[i - 1] + a[i];
}
RMQ_pos_init(n, a); solve(1, n);
if (qwe)printf("\n");
else qwe = true;
printf("%lld\n", ans);
printf("%d %d\n", L, R);
}
return 0;
}
codefores7.26场
C题:推一个数学公式,只是写出来了,有点成就感,贴一下
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
int a[1000], b[1000];
int main() {
ll n, m; scanf("%I64d%I64d", &n, &m);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
int c; scanf("%d", &c);
for (int i = 0; i < n-1; i++) scanf("%d", &b[i]);
b[n - 1] = c;
double tot = m;
int flag = 0;
for (int i = n-1; i >= 0; i--) {
if (b[i] <= 1) { flag = 1; break; }
double tmp = tot / double(b[i] - 1);
tot += tmp;
if (a[i] <= 1) { flag = 1; break; }
tmp =double( tot / (a[i] - 1));
tot += tmp;
}
if (flag)printf("-1");
else printf("%.14f\n", double(tot - m));
return 0;
}
B题:
题意:有m袋食物,袋中的食物可能相同,n个人,每个人只能分一种食物,不同的人可以分同种食物,问最多吃几天
解法:竟然没想出来。。。
//哪个人吃哪种食物并不重要,重点是这些食物如果那么多天吃的话够不够分
#include<iostream>
using namespace std;
int A[110];
int main(){
int n, m, t = 0, x, k, y;
cin >> n >> m;
for (x = 0; x < m; x++){cin >> k;A[k]++;}
for (x = 1; x < 101; x++){//枚举天数
int s = 0;
for (y = 1; y <= 100; y++)s += A[y] / x;//y种食物分成k份够几个人吃
if (s >= n)t = x;
}
cout << t << endl;
return 0;
}
E题:
题意:有n种钱,每种钱的数量是无限的,给你一个进制数k,问钱能组成的所有面额%k能有几种情况
解法:欧几里得算法,注意只需要列举1~k的情况就行
看一下代码就懂了
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;
const int maxn = 1e9 + 7;
ll a[1000000 + 10];
ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a%b); }
set<ll>s;
int main() {
ll n, k; scanf("%I64d%I64d", &n, &k);
ll num; scanf("%I64d", &num);
for (int i = 1; i < n; i++) {
scanf("%d", &a[i]); num = gcd(num, a[i]);
}
ll c = 0;
//枚举不同的个位数,因为其他n位数乘出来的结果也肯定不外乎这几种情况
while (++c<=k) s.insert(c*num%k);
printf("%I64d\n", 1ll*s.size());
for (auto i : s) printf("%I64d ", i);
return 0;
}