The 15th Heilongjiang Provincial Collegiate Programming Contest--队内训练

博客分享了比赛中几道题的解题情况。如A题求图形面积,靠直觉解题;B题是线段树结合并查集降低复杂度;C题枚举长度比较字符串;F题利用步兵相对位置建边,原思路被hack;G题通过找规律解题。
题目ABCDEFGHIJKL
Solved♠\spadesuit∙\bullet♠\spadesuit♠\spadesuit∙\bullet♠\spadesuit∙\bullet♠\spadesuit

♠\spadesuit比赛时通过;∙\bullet:赛后通过;⊘\oslash :比赛时尝试了未通过;∘\circ:比赛时未尝试


A. August

开场签到题,题意就是求题目中的图形面积,一眼看下去,直觉爱心的下半部分可以构成一个长方形。试了下答案对了。瞎搞才是硬道理

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
template<typename T>
inline void rd(T& x)
{
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}

const int N = 2e3 + 10;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double pi = acos(-1);
int main() {
#ifdef _DEBUG
	//	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif
	int T;
	rd(T);
	while (T--) {
		ll a, b; rd(a), rd(b);
		ll res1 = a * b * 4;
		ll res2 =  a * a;
		double sum = res1 + pi * res2;
		printf("%.8f\n", sum);
	}
	return 0;
}


B. Bills of Paradise

这题一看就是一道线段树,但是训练的时候太困了实在没有复杂度低的想法。之后看了博客才恍然大悟,只要结合并查集就可以大大降低这题的复杂度(反正训练的时候就是想不到的呜呜)。
题意比较复杂,下面慢慢解释。
题目给定一个函数,用其产生nnn个数,存入数组aaa
接下来给出qqq个询问。

  • 询问FFF,输入一个数xxx,输出数组aaa中大于等于xxx的第一个未被标记的数,若没有则输出100000000000010000000000001000000000000
  • 询问DDD,输入一个数xxx,标记数组aaa中大于等于xxx的第一个未被标记的数,若没有则跳过这步操作。
  • 询问CCC,输入一个数xxx,查询数组aaa中小于等于xxx的所有未标记数之和,若没有则输出0。
  • 询问RRR,输入一个数xxx,清楚数组aaa中小于等于xxx的数的所有标记,若没有标记过,则不操作。(题目中提到这个操作总共只会进行10次)

这里是一些前置操作

  • 用题目中给的函数进行aaa的生成后,将其sortsortsort从小到大排序。
  • 维护数组aaa的前缀和。
  • 初始化并查集。并查集维护这个数是否被标记过,若没有被标记过其祖先就是自己本身。
  • 接下来进行建树,树的初始值为000,因此不需要pushuppushuppushup的操作,都赋值为0即可。树中保存的是标记的过的数的值之和,因此没被标记过的数在线段树中的权值也就自然是000了。

接下来是四种询问的具体细分

  • 询问FFF,先使用lower_boundlower\_boundlower_bound二分查找到大于等于xxx的数,然后通过并查集查找其祖先,也就是没有被标记过的哪个数。若找到则直接输出这个数,没有找到就输出100000000000010000000000001000000000000。没有找到共有两种情况:第一种情况是就找不到一个数是大于等于xxx的,也就是lower_boundlower\_boundlower_bound出来的下标等于n+1n+1n+1(具体怎么lower_boundlower\_boundlower_bound这一点在代码中有体现);第二种情况是大于等于xxx的数每个都被标记过了,也就是他们的祖先是n+1n+1n+1
  • 询问DDD,同样使用lower_boundlower\_boundlower_bound二分查找到大于等于xxx的数,若数组aaa中能找到这样的数则继续进行操作。若其本身就没有被标记过,则直接标记其本身(线段树中进行updateupdateupdate),并将其祖先设置为他的后一个数。若其本身被标记过并且其祖先是n+1n+1n+1,说明找不到没有标记过的数了,则跳过此次操作。如果其祖先不是n+1n+1n+1,则将其祖先进行标记(线段树中进行updateupdateupdate),并且将其祖先的祖先设置为其祖先的后面一个数。
  • 询问CCC,先使用upper_boundupper\_boundupper_bound二分查找到小于等于xxx的数,若找不到,也就是找到的则输出0。找到了就将算好的前缀和减去前面被标记过的数之和(使用线段树的queryqueryquery求和)就可以得到答案输出了。
  • 询问RRR,同样使用upper_boundupper\_boundupper_bound二分查找到小于等于xxx的数,若找不到,就不操作了。找到了就updateupdateupdate清空区间内的所有值并将这个区间的祖先都还原为本身即可。

具体细节看代码,记得要开longlonglonglonglonglong

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <functional>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <vector>
#define lowbit(x) ((x) & -(x))
#define endl "\n"
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e6 + 10, NN = 2e3 + 10, INF = 0x3f3f3f3f, LEN = 20;
const ll MOD = 1e9 + 7;
const ull seed = 31;
inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
int n;
int fa[N];
ll a[N], sum[N], tree[N << 2], ERROR;
ull k1, k2;
ull xorShift128Plus() {
    ull k3 = k1, k4 = k2;
    k1 = k4;
    k3 ^= k3 << 23;
    k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
    return k2 + k4;
}
int find(int x) {
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y) {
    int fx = find(x), fy = find(y);
    fa[fx] = fy;
}
void gen() {
    scanf("%d %llu %llu", &n, &k1, &k2);
    for (int i = 1; i <= n; i++)
        a[i] = xorShift128Plus() % 999999999999 + 1;
}
inline void pushup(int rt) {
    tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}
void buildtree(int rt, int l, int r) {
    tree[rt] = 0;
    if (l == r)
        return;
    int mid = (l + r) >> 1;
    buildtree(rt << 1, l, mid);
    buildtree(rt << 1 | 1, mid + 1, r);
}
void update(int rt, int l, int r, int id, ll val) {
    if (l == r && l == id) {
        tree[rt] += val;
        return;
    }
    int mid = (l + r) >> 1;
    if (id <= mid)
        update(rt << 1, l, mid, id, val);
    else
        update(rt << 1 | 1, mid + 1, r, id, val);
    pushup(rt);
}
void update_clear(int rt, int l, int r, int ql, int qr) {
    if (l == r) {
        tree[rt] = 0;
        return;
    }
    int mid = (l + r) >> 1;
    if (ql <= mid)
        update_clear(rt << 1, l, mid, ql, qr);
    if (qr > mid)
        update_clear(rt << 1 | 1, mid + 1, r, ql, qr);
    pushup(rt);
}
ll query(int rt, int l, int r, int ql, int qr) {
    if (l >= ql && r <= qr)
        return tree[rt];
    int mid = (l + r) >> 1;
    ll ans = 0;
    if (ql <= mid)
        ans += query(rt << 1, l, mid, ql, qr);
    if (qr > mid)
        ans += query(rt << 1 | 1, mid + 1, r, ql, qr);
    return ans;
}
void init() {
    ERROR = 1e12;
    sum[0] = 0;
    for (int i = 1; i <= n; i++) {
        fa[i] = i;
        sum[i] = sum[i - 1] + a[i];
    }
    fa[n + 1] = n + 1;
}
int main() {
    // ios::sync_with_stdio(false);
    // cin.tie(0);
    // cout.tie(0);
    // freopen("input.txt", "r", stdin);
    // freopen("output.txt", "w", stdout);
    gen();
    sort(a + 1, a + 1 + n);
    init();
    buildtree(1, 1, n);
    int q;
    scanf("%d", &q);
    while (q--) {
        char op;
        ll x;
        scanf(" %c%lld", &op, &x);
        if (op == 'F') {
            int id = lower_bound(a + 1, a + 1 + n, x) - a;
            if (id == n + 1) {
                printf("%lld\n", ERROR);
                continue;
            }
            int pos = find(id);
            if (pos == n + 1)
                printf("%lld\n", ERROR);
            else
                printf("%lld\n", a[pos]);
        } else if (op == 'D') {
            int id = lower_bound(a + 1, a + 1 + n, x) - a;
            if (id == n + 1)
                continue;
            if (id == find(id)) {  // 未被标记
                update(1, 1, n, id, a[id]);
                merge(id, id + 1);
            } else {
                int pos = find(id + 1);
                if (pos == n + 1)
                    continue;
                update(1, 1, n, pos, a[pos]);
                merge(pos, pos + 1);
            }
        } else if (op == 'C') {
            int id = upper_bound(a + 1, a + 1 + n, x) - a - 1;
            if (id == 0) {
                printf("0\n");
                continue;
            }
            ll ans = query(1, 1, n, 1, id);
            ll temp = sum[id];
            printf("%lld\n", temp - ans);
        } else if (op == 'R') {
            int id = upper_bound(a + 1, a + 1 + n, x) - a - 1;
            if (id == 0)
                continue;
            update_clear(1, 1, n, 1, id);
            for (int i = 1; i <= id; i++)
                fa[i] = i;
        }
    }
    return 0;
}

C. Cornelia Street

题意就是找出两个相同长度的字符串 AAABBB ,满足 AAABBBAAa能够满足题目给出的字符串,其中 aaaAAA 的前缀。

  • 一开始被这道题的数据范围给吓到了,但还是去试了一下看能不能过,就是枚举长度一个一个比较过去,没想到…还真的AC了,有惊有险
  • 枚举长度 lll ,后面在比较串的时候一个一个比较过去就可以了,基本的操作。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
template<typename T>
inline void rd(T& x)
{
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}

const int N = 8e5 + 10;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
char s[N];
int pos, len, now;
bool check1(int l) {
	now = (len / 2) / l * l;
	bool flag = false;
	for (int i = l; i < len; ++i) {
		if (s[i % l] == s[i]) {
			flag = true;
		}
		else {
			now = i / l * l;
			return true;
		}
	}
	return true;
}
bool check2(int l) {
	int nxt = len;
	bool flag = false;
	for (int i = now+l; i < len; ++i) {
		if (s[i % l + now] == s[i]) {
			flag = true;
		}
		else {
			int pos = i / l * l;
			for (int j = pos; j < len; ++j) {
				if (s[j % l] != s[j]) {
					return false;
				}
			}
			return true;
		}
	}
	return true;
}
int main() {
#ifdef _DEBUG
	//	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif
//	int T;
//	rd(T);
//	while (T--) {
		scanf(" %s", s);
		len = strlen(s);
		for (int i = 1; i <= len / 2; ++i) {
			if (check1(i) && check2(i)) {
				for (int j = 0; j < i; ++j) printf("%c", s[j]); printf(" ");
				for (int j = 0; j < i; ++j) printf("%c", s[j + now]);
				puts("");
				break;
			}
		}
//	}
	return 0;
}


F. False God

  • 根据题目意思,每个步兵都会向下移动一格,但值得注意的是,这些步兵的相对位置并不会发生改变。
  • 利用相对位置不变的特性,可以把一个步兵和另外的步兵建立一个单向边,表示若金在步兵 uuu 的位置时,金可以到步兵 vvv 的位置并把步兵 vvv 吃掉
  • 而金在一个位置能够吃到步兵 vvv 的条件是 abs(xu−xv)−(yv−yu)≤1abs(x_u-x_v)-(y_v-y_u)≤1abs(xuxv)(yvyu)1, 这个可以根据金的移动规律推得。
  • 在知道以上情况后,直接暴力 O(n2)O(n^2)O(n2) 对所有点进行建立单向边即可。随后跑一遍 dfsdfsdfs 得出最大深度即为答案。

上面的思路被 hack 了,暂未更新。

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
template<typename T>
inline void rd(T& x)
{
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}

const int N = 1e3 + 10;
const int M = 1e7 + 10;
const ll MAX = 1e12;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
int head[N], cntE;
struct edge {
	int next, to;
}e[M];
void add(int u, int v) {
	e[cntE].to = v;
	e[cntE].next = head[u];
	head[u] = cntE++;
}
int n;
ll x[N], y[N];
ll a[N];
bool vis[N];
ll dfs(int x) {
	vis[x] = true;
	for (int i = head[x]; ~i; i = e[i].next) {
		int v = e[i].to;
		if (vis[v]) {
			a[x] = max(a[x], a[v] + 1);
			continue;
		}
		a[x] = max(a[x], dfs(v) + 1);
	}
	return a[x];
}
int main() {
#ifdef _DEBUG
	//	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif
	int T; rd(T);
	while (T--) {
		rd(x[0]); rd(y[0]);
		rd(n);
		for (int i = 1; i <= n; ++i) {
			rd(x[i]); rd(y[i]);
		}
		memset(head, -1, sizeof(int)*(n+10)); cntE = 0;
		for (int i = 1; i <= n; ++i) {
			if (abs(x[0] - x[i]) - (y[i] - y[0]) <= 1) {
				add(0, i);
			}
		}
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= n; ++j) {
				if (i == j) continue;
				if (abs(x[i] - x[j]) - (y[j] - y[i]) <= 1) {
					add(i, j);
				}
			}
		}
		for (int i = 1; i <= n; ++i) a[i] = 1;
		memset(vis, false, sizeof(bool) * (n + 10));
		a[0] = 0;
		printf("%lld\n", dfs(0)-1);
	}
	return 0;
}


G. Goodbye

老实说,这题…emmm…题目三个人都没读懂什么意思,后来我强行去看答案找规律发现
*

  • 4=22,8=23,666=2∗32∗374=2^2,8=2^3,666=2*3^2*374=22,8=23,666=23237
  • 由上述规律看出来,每次有值的答案都选了最大的两个因子;并且由博弈来看,挑出两个因子的数只能进行两次操作,貌似这个猜测是正确的,然后瞎搞了一番,AC…瞎搞才是硬道理
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
template<typename T>
inline void rd(T& x)
{
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}

const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
int prime[N]; bool isprime[N] = { false };
int cnt;
void init() {
	cnt = 0;
	isprime[0] = isprime[1] = true;
	for (int i = 2; i <= N - 5; ++i) {
		if (!isprime[i]) {
			prime[++cnt] = i;
		}
		for (int j = 1; j <= cnt && prime[j] * i <= (N - 10); ++j) {
			isprime[prime[j] * i] = true;
			if (i % prime[j] == 0) break;
		}
	}
}
int a[N];
vector<pii>vec;
ll fp(ll x, ll y) {
	ll ans = 1;
	while (y) {
		if (y & 1) ans *= x;
		x *= x;
		y >>= 1;
	}
	return ans;
}
bool check() {

	bool flag = false;
	int cnt = 0;
	for (auto v : vec) {
		cnt += v.second;
	}
	if (cnt < 3) return false;
	int ans = vec[vec.size() - 1].first;
	if (vec[vec.size() - 1].second > 1) ans *= vec[vec.size() - 1].first;
	else ans *= vec[vec.size() - 2].first;
	printf("%d\n", ans);
	return true;
	
}
int main() {
#ifdef _DEBUG
	//	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif
	init();
	int T;
	rd(T);
	while (T--) {
		int n; rd(n);
		vec.clear(); 
		if (n == 1) {
			puts("0");
			continue;
		}
		if (!isprime[n]) {
			puts("0");
			continue;
		}
		for (int i = 1; i <= cnt && prime[i] * prime[i] <= n; ++i) {
			if (n % prime[i] == 0) {
				int tot = 0;
				while (n % prime[i] == 0) ++tot, n /= prime[i];
				vec.push_back({ prime[i],tot });
			}
		}
		if (n > 1) vec.push_back({ n,1 });
		if (!check()) puts("-1");

	}
	return 0;
}


当Nginx配置文件测试失败,提示 `bind() to 0.0.0.0:80 failed (10013: An attempt was made to access a socket in a way forbidden by its access permissions)` 时,可按以下方法解决: #### 查看并结束占用80端口的进程 1. 打开命令提示符(CMD),输入以下命令查询占用80端口的进程: ```bash netstat -aon|findstr "80" ``` 此命令会列出所有占用80端口的进程及其对应的PID(进程ID)。例如: ```plaintext TCP 0.0.0.0:80 0.0.0.0:0 LISTENING 7532 ``` 这里的 `7532` 就是占用80端口的进程的PID [^3]。 2. 查看该PID对应的任务,输入命令: ```bash tasklist|findstr "7532" ``` 此命令会显示PID为7532的进程的详细信息,例如: ```plaintext nginx.exe 7532 Console 1 7,440 K ``` 3. 结束对应任务。可以使用任务管理器手动结束该进程,也可以在命令提示符中输入以下命令结束进程: ```bash taskkill /F /PID 7532 ``` 其中,`/F` 表示强制结束进程,`/PID` 后面跟要结束的进程的PID [^3]。 4. 结束占用80端口的进程后,再次启动Nginx。 #### 以管理员身份运行Nginx 在某些情况下,由于权限不足,Nginx无法绑定到80端口。可以尝试以管理员身份运行Nginx。具体操作是:右键点击Nginx的可执行文件(通常是 `nginx.exe`),选择“以管理员身份运行”。 #### 修改Nginx监听端口 如果无法解决80端口被占用的问题,或者没有足够的权限绑定到80端口,可以修改Nginx的监听端口。打开Nginx的配置文件(通常是 `nginx.conf`),找到 `listen` 指令,将端口号修改为其他未被占用的端口,例如 `8080`: ```nginx server { listen 8080; server_name localhost; # 其他配置... } ``` 修改完成后,保存配置文件,然后重新启动Nginx。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值