2018 CCPC 女生专场
可能是史上最弱的验题人——
Problem A
(小)模拟。
#include <bits/stdc++.h> using namespace std; int T; int main(){ scanf("%d", &T);
while (T--){
char rk[10];
scanf("%s", rk);
int n = strlen(rk);
for (int i = 0; i < 3 - n; ++i)
putchar(' ');
printf("%s", rk);
putchar('|');
char S[20];
scanf("%s", S);
printf("%s", S);
n = strlen(S);
for (int i = n; i < 16; ++i)
putchar(' ');
putchar('|');
scanf("%s", rk);
printf("%s", rk);
scanf("%s", rk);
if (rk[0] == 'R' && rk[1] == 'u') {
int x;
scanf("%d", &x);
putchar('|');
putchar('[');
for (int i = 1; i <= x; ++i)
putchar('X');
for (int i = 1; i <= 10 - x; ++i)
putchar(' ');
putchar(']');
}
else{
if (rk[0] == 'F') {
rk[0] = 'A';
rk[1] = 'C';
rk[2] = '*';
rk[3] = '\0';
}
putchar('|');
putchar('[');
for (int i = 0; i < 4; ++i)
putchar(' ');
printf("%s", rk);
int n = strlen(rk);
for (int i = 1; i <= 6 - n; ++i)
putchar(' ');
putchar(']');
}
puts("");
}
}
Problem B
观察到$d$大于$316$的质因子最多只有一个,那么先判掉$<= 316$的所有质因子,搞个前缀和就可以了。
然后特判大于$316$的质因子即可,方法有很多。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define fi first
#define se second
#define MP make_pair typedef long long LL;
typedef pair <int, int> PII; const int N = 1e5 + 10; int T;
int s[70][N];
int a[N];
int n, m;
int re[N];
int c[N];
int id = 0;
int prime[N], fp[N], val[N];
vector <PII> pri[N];
vector <int> v[N]; int main(){ rep(i, 2, 1e5){
for (int j = i + i; j <= 1e5; j += i) c[j] = 1; } rep(i, 2, 1e5){
if (!c[i]){
++id;
prime[id] = i;
fp[i] = id;
}
} rep(i, 1, 1e5) val[i] = i; rep(i, 2, 1e5) if (!c[i]){
for (int j = i + i; j <= 1e5; j += i){
int cnt = 0;
while (val[j] % i == 0) val[j] /= i, ++cnt;
pri[j].push_back(MP(i, cnt));
if (i > 316) re[j] = i;
}
} rep(i, 2, 1e5) if (val[i] > 1){
pri[i].push_back(MP(i, 1));
if (val[i] > 316) re[i] = val[i];
} scanf("%d", &T);
while (T--){
scanf("%d%d", &n, &m);
memset(s, 0, sizeof s);
memset(a, 0, sizeof a); rep(i, 1, 1e5 + 1) v[i].clear();
rep(i, 1, n){
int x;
scanf("%d", &x);
for (auto u : pri[x]){
int nowid = fp[u.fi], nowcnt = u.se;
if (u.fi <= 316){
s[nowid][i] += nowcnt;
}
} if (re[x]){
a[i] = re[x];
v[re[x]].push_back(i);
}
} rep(i, 1, 67){
rep(j, 1, n) s[i][j] += s[i][j - 1];
} while (m--){
int x, y, z;
scanf("%d%d%d", &x, &y, &z); int ret = 1;
for (auto u : pri[z]){
if (u.fi > 316) break;
int nowid = fp[u.fi], nowcnt = u.se;
ret &= (s[nowid][y] - s[nowid][x - 1] >= u.se);
} if (re[z]){
int num = re[z];
int sz = (int)v[num].size();
if (sz == 0) ret = 0;
else{ int l = 0, r = sz - 1;
if (v[num][r] < x){
ret = 0;
} else{
while (l + 1 < r){
int mid = (l + r) >> 1;
if (v[num][mid] >= x){
r = mid;
} else{
l = mid + 1;
}
} int t;
if (v[num][l] >= x) t = l;
else t = r; int minpos = v[num][t];
ret &= (minpos <= y);
}
}
} puts(ret ? "Yes" : "No");
}
} return 0; }
Problem C
二分一下就可以了,具体实现的时候要尽量避开log函数,否则会因为精度问题产生误差。
#include <bits/stdc++.h> using namespace std; #define ll long long ll LOG(ll x){
ll ans = 0;
while (true) {
if (x == 1) return ans;
else if (x == 2) return ans + 1;
x = x - (x / 2);
ans++;
}
} bool check(ll n, ll k, int a, int b) {
ll m = LOG(n);
if (m == 0) return 1;
ll tmp = 1;
while (a--) {
if (k / tmp < n) return 0;
tmp *= n;
}
while (b--) {
if (k / tmp < m) return 0;
tmp *= m;
}
return 1;
} int a, b;
int T;
ll k; int main() { scanf("%d", &T); while (T--) {
scanf("%d%d%I64d", &a, &b, &k);
ll l = 1, r = k, ans;
while (l <= r) {
ll mid = (l + r) / 2;
if (check(mid, k, a, b)) ans = mid, l = mid + 1;
else r = mid - 1;
}
printf("%I64d\n", ans);
}
return 0;
}
Problem D
难题。考虑DP,设$f[i][j][x][y]$表示走到第$i$行第$j$列,有$x$个路径上本来应该计入答案的格子没有计入答案,
有$y$个本不属于这条路径的格子计入了答案的最大值。
走到$a_{i,j}$的时候把那些肯定不可能走到的格子排序然后贪心转移即可。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second typedef long long LL; const int N = 53;
const int K = 27; int T;
int f[N][N][K][K], a[N][N], c[N];
int n, m, k;
int ans = 0; inline void up(int &x, int y){
if (x < y) x = y;
} int main(){ scanf("%d", &T);
while (T--){
scanf("%d%d%d", &n, &m, &k);
rep(i, 1, n){
rep(j, 1, m) scanf("%d", a[i] + j);
} memset(f, 0xc0, sizeof f); f[1][1][0][0] = a[1][1];
f[1][1][0][1] = 0; rep(i, 1, n){
rep(j, 1, m){
rep(q, 1, m){
if (q != j){
if (q < j){
c[q] = a[i + 1][q];
} else{
c[q] = a[i][q];
}
} else{
c[q] = 0;
}
} sort(c + 1, c + m + 1, greater<int>()); rep(q, 1, m){
c[q] += c[q - 1];
} rep(p, 0, k){
rep(q, 0, k){
up(f[i][j + 1][p][q], f[i][j][p][q] + a[i][j + 1]);
up(f[i][j + 1][p][q + 1], f[i][j][p][q]);
rep(z, 0, m - 1){
if (p + z > k) break;
up(f[i + 1][j][p + z][q], f[i][j][p][q] + a[i + 1][j] + c[z]);
up(f[i + 1][j][p + z][q + 1], f[i][j][p][q] + c[z]);
}
}
}
}
} ans = 0;
rep(i, 0, k) up(ans, f[n][m][i][i]);
printf("%d\n", ans);
} return 0;
}
Problem E
直接上最短路就可以了,注意计算对数要用整数运算,跑最短路要堆优化Dij
(不是这个算法的全被卡了)
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second typedef long long LL;
typedef pair <int, int> PII; const int N = 2e5 + 10; struct node{
int u;
LL w;
friend bool operator < (const node &a, const node &b){
return a.w > b.w;
}
}; int T;
int b[N];
int inqueue[N];
int n, m;
int ret;
LL d[N];
LL a[N];
vector <PII> v[N]; void dij(){
priority_queue <node> q;
static bool vis[N];
rep(i, 1, n + 1) d[i] = 4e18, vis[i] = false;
q.push({1, 1});
d[1] = 1; while (!q.empty()){
int u = q.top().u;
q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (auto edge : v[u]){
int xx = edge.fi, id = edge.se, wb = b[id];
LL wa = a[id]; if (d[u] + wa < d[xx] && (d[u] + wa) / d[u] >= (1ll << wb)){
d[xx] = d[u] + wa;
q.push({xx, d[xx]});
}
}
}
} int main(){ scanf("%d", &T);
while (T--){
rep(i, 0, n + 1) v[i].clear();
scanf("%d%d", &n, &m);
rep(i, 1, m){
int x, y;
scanf("%d%d%lld%d", &x, &y, a + i, b + i);
v[x].push_back(MP(y, i));
} dij(); if (d[n] == 4e18){
puts("-1");
continue;
} ret = 0;
while (d[n]){
++ret;
d[n] /= 2ll;
} printf("%d\n", ret - 1);
} return 0;
}
Problem F
勇敢地直接上树上莫队,对$01$数组分块,同时维护每个块内$0$的个数,然后注意一些细节,就可以AC了。
在HDOJ上大概要跑$10s$。
二分答案+主席树方法待补。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i) const int N = 2e5 + 10;
const int A = 19; int T;
int n, m;
int cnt, bs, g, top, ti, ret;
int a[N], b[N], c[N], bg[N];
int f[N][A], deep[N], dfn[N];
int belong[N], stk[N];
int vis[N], s[N], ans[N];
int L[N], R[N];
int bl[N], bss, blocknum;
int extra;
int exist[N];
vector <int> v[N]; struct node{
int x, y, id;
friend bool operator < (const node &a, const node &b){
return belong[a.x] == belong[b.x] ? dfn[a.y] < dfn[b.y] : belong[a.x] < belong[b.x];
}
} q[N]; int LCA(int a, int b){
if (deep[a] < deep[b]) swap(a, b);
for (int i = 0, delta = deep[a] - deep[b]; delta; delta >>= 1, ++i)
if (delta & 1) a = f[a][i]; if (a == b) return a;
dec(i, 18, 0) if (f[a][i] ^ f[b][i]){
a = f[a][i];
b = f[b][i];
}
return f[a][0];
} void dfs(int x, int fa, int dep){
deep[x] = dep;
if (fa){
f[x][0] = fa;
for (int i = 0; f[f[x][i]][i]; ++i)
f[x][i + 1] = f[f[x][i]][i];
} for (auto u : v[x]){
if (u == fa) continue;
dfs(u, x, dep + 1);
}
} void dfs(int x, int fa){
dfn[x] = ++ti;
int bot = top;
for (auto u : v[x]){
if (u == fa) continue;
dfs(u, x);
if (top - bot >= bs){
++g;
while (top ^ bot) belong[stk[top--]] = g;
}
}
stk[++top] = x;
} void rev(int x){
if (c[a[x]] == 0) --bg[bl[a[x]]];
else ++bg[bl[a[x]]]; c[a[x]] ^= 1;
vis[x] ^= 1;
} void work(int x, int y){
for (; x ^ y; ){
if (deep[x] < deep[y]) swap(x, y);
rev(x);
x = f[x][0];
}
} void init(){
rep(i, 0, n + 1) v[i].clear();
memset(c, 0, sizeof c);
memset(f, 0, sizeof f);
memset(vis, 0, sizeof vis);
memset(exist, 0, sizeof exist);
ti = 0;
top = 0;
g = 0;
cnt = 0;
bs = 0;
ret = 0;
} int main(){ scanf("%d", &T);
while (T--){
scanf("%d%d", &n, &m);
init();
rep(i, 1, n) scanf("%d", a + i), b[i] = a[i]; rep(i, 1, n) exist[a[i]] = 1; rep(i, 1, 200002) if (!exist[i]){
extra = i;
break;
} sort(b + 1, b + n + 1);
cnt = unique(b + 1, b + n + 1) - b - 1;
rep(i, 1, n) a[i] = lower_bound(b + 1, b + cnt + 1, a[i]) - b; rep(i, 2, n){
int x, y;
scanf("%d%d", &x, &y);
v[x].push_back(y);
v[y].push_back(x);
} dfs(1, 0, 0);
bs = sqrt(n);
bss = sqrt(cnt);
dfs(1, 0); rep(i, 1, cnt) bl[i] = (i - 1) / bss + 1;
blocknum = bl[cnt]; rep(i, 1, blocknum) L[i] = 1e9, R[i] = 0;
rep(i, 1, cnt){
L[bl[i]] = min(L[bl[i]], i);
R[bl[i]] = max(R[bl[i]], i);
} rep(i, 1, blocknum) bg[i] = R[i] - L[i] + 1; rep(i, 1, m){
scanf("%d%d", &q[i].x, &q[i].y);
q[i].id = i;
if (dfn[q[i].x] > dfn[q[i].y]) swap(q[i].x, q[i].y);
} sort(q + 1, q + m + 1);
q[0].x = q[0].y = 1; rep(i, 1, m){
work(q[i - 1].x, q[i].x);
work(q[i - 1].y, q[i].y);
int lca = LCA(q[i].x, q[i].y);
rev(lca); int now = 0, fg = 0, ret = 200001;
rep(i, 1, blocknum){
now += bg[i];
if (now > 0){
fg = 1;
rep(j, L[i], R[i]) if (!c[j]){
ret = j;
break;
} break;
}
} if (fg) ans[q[i].id] = min(b[ret], extra);
else ans[q[i].id] = extra; rev(lca);
} rep(i, 1, m) printf("%d\n", ans[i]); }
return 0;
}
Problem G
全场题。
#include <bits/stdc++.h> using namespace std; const int N = 505, INF = 0x3f3f3f3f; int t; int main(){ scanf("%d",&t);
for (int id=1001;id<=1000+t;id++){
int n,m; scanf("%d%d",&n,&m);
int ans1=INF,ans2=INF;
for (int i=1;i<=n;i++)
{
int x; scanf("%d",&x);
ans1=min(ans1,x);
}
for (int i=1;i<=m;i++)
{
int x; scanf("%d",&x);
ans2=min(ans2,x);
}
printf("Problem %d:\n",id);
printf("Shortest judge solution: %d bytes.\n",ans1);
if (ans2==INF)
printf("Shortest team solution: N/A bytes.\n");
else
printf("Shortest team solution: %d bytes.\n",ans2);
}
}
Problem H
难题,题目给定的是一个二分图模型,
我们需要把这个二分图的模型转化成基环生成树森林。
然后从高位到低位分治,把当前集合根据最高位$0$或$1$分成两个不同的集合。
显然集合内部连边方案更优。可以证明当两个集合大小都超过$3$时,没有横跨集合的边。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second typedef long long LL; const int N = 3e5 + 10; int T;
int n; LL solve(int d, vector<int> a){
if (d < 0) return 0;
int n = (int)a.size();
if (n <= 4){
LL ret = 0;
vector <int> c;
rep(i, 0, n - 1) rep(j, i + 1, n - 1) c.push_back(a[i] ^ a[j]);
sort(c.begin(), c.end());
for (int i = 0; i < n && i < n * (n - 1) / 2; i++) ret += c[i];
return ret;
} vector <int> b[2];
rep(i, 0, n - 1) b[a[i] >> d & 1].push_back(a[i]);
a.clear();
LL ret = solve(d - 1, b[0]) + solve(d - 1, b[1]);
int t[2] = {(int)b[0].size(), (int)b[1].size()};
if (t[0] && t[1] && (t[0] < 3 || t[1] < 3)){
int mi = 2e9;
rep(i, 0, t[0] - 1) rep(j, 0, t[1] - 1) mi = min(mi, b[0][i] ^ b[1][j]);
ret += mi;
} return ret;
} int main(){ scanf("%d", &T); while (T--){
scanf("%d", &n);
vector <int> a(n);
rep(i, 0, n - 1) scanf("%d", &a[i]);
printf("%lld\n", solve(29, a));
} return 0;
}
Problem I
其实这题就没几行……完全不需要后缀数组。
直接从后往前扫一遍,递推就可以了。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second typedef long long LL; const int N = 1e6 + 10; int T;
int n;
char s[N], ans[N]; int main(){ scanf("%d", &T);
while (T--){
scanf("%d", &n);
scanf("%s", s + 1); ans[n] = '>';
dec(i, n - 1, 1){
if (s[i] < s[i + 1]) ans[i] = '<';
else if (s[i] > s[i + 1]) ans[i] = '>';
else ans[i] = ans[i + 1];
} ans[n] = '\0';
puts(ans + 1);
} return 0;
}
Problem J
由于数据是随机的,所以直接枚举回文重心往两边不断延伸(也就是直接暴力)就可以了。
VP的时候直接尝试只枚举长度 $<= 3$的回文串,直接AC了。
赛前预计AC $≈$ $30$,赛场上去掉打星队AC $= 3$
……
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100005; vector<int>link[N];
int a[N];
int cnt[N];
int t; int main(){ scanf("%d",&t);
while(t--)
{
int n; scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
ll ans=n;
for (int i=1;i<n;i++)
{
int x,y; scanf("%d%d",&x,&y);
link[x].push_back(y);
link[y].push_back(x);
if (a[x]==a[y]) ans++;
}
for (int i=1;i<=n;i++)
{
for (int v:link[i])
{
ans+=cnt[a[v]];
cnt[a[v]]++;
}
for (int v:link[i]) cnt[a[v]]--;
}
printf("%lld\n",ans);
for (int i=1;i<=n;i++) link[i].clear();
}
}
Problem K
难题。考虑容斥。
计数的时候对于某个格子,如何减掉重复计算的答案?
把这个矩阵往左,往上,往左上各推一个单位,就可以很巧妙地容斥了。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second typedef long long LL; const int N = 100005;
const int M = 1005; inline LL calc(int n){ return 1ll * n * (n - 1) * (n - 2) / 6;} int T;
int n;
int x[N][2], y[N][2];
int c[M][M];
LL ret[4]; int main(){ scanf("%d", &T);
while (T--){
scanf("%d", &n);
rep(i, 1, n) rep(j, 0, 1) scanf("%d%d", x[i] + j, y[i] + j);
memset(ret, 0, sizeof ret);
rep(op, 0, 3){
memset(c, 0, sizeof c);
rep(i, 1, n){
int sx = -(op / 2), sy = -(op % 2);
c[x[i][0]][y[i][0]]++;
c[x[i][0]][y[i][1] + sy + 1]--;
c[x[i][1] + sx + 1][y[i][0]]--;
c[x[i][1] + sx + 1][y[i][1] + sy + 1]++;
} rep(i, 1, M - 1){
rep(j, 1, M - 1){
c[i][j] += c[i - 1][j] + c[i][j - 1] - c[i - 1][j - 1];
ret[op] += calc(c[i][j]);
}
}
}
printf("%lld\n", ret[0] - ret[1] - ret[2] + ret[3]);
} return 0;
}
2018 CCPC 女生专场的更多相关文章
- 2018 CCPC 女生赛 hdoj6287 口算训练
题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=6287 Summarize: 1.分解质因数: 2.二分查找函数lower_bound与upper_bo ...
- 2018 CCPC 女生赛 hdoj6288 缺失的数据范围
题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=6288 Summarize:1.二分查找答案: 2.自带log函数精度不够,需自己写: 3.注意二分递归 ...
- HDU 6024(中国大学生程序设计竞赛女生专场1002)
这是CCPC女生专场的一道dp题.大佬们都说它简单,我并没有感到它有多简单. 先说一下题意:在一条直线上,有n个教室,现在我要在这些教室里从左到右地建设一些作为糖果屋,每个教室都有自己的坐标xi 和建 ...
- 2018 CCPC网络赛
2018 CCPC网络赛 Buy and Resell 题目描述:有一种物品,在\(n\)个地点的价格为\(a_i\),现在一次经过这\(n\)个地点,在每个地点可以买一个这样的物品,也可以卖出一个物 ...
- 2018 CCPC 桂林游记
TYPE: Onsite Contest NAME: 2018 - CCPC - Guilin PLAT: HUSTOJ TIME: 2018/10/28 09:00-14:00 CST LOCA: ...
- 2017中国大学生程序设计竞赛 - 女生专场 Deleting Edges(思维+最短路)
Deleting Edges Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) ...
- 2017中国大学生程序设计竞赛 - 女生专场 Happy Necklace(递推+矩阵快速幂)
Happy Necklace Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) ...
- 2017中国大学生程序设计竞赛 - 女生专场(Graph Theory)
Graph Theory Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)To ...
- 2017中国大学生程序设计竞赛 - 女生专场(dp)
Building Shops Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) To ...
随机推荐
- PICT:基于正交法的软件测试用例生成工具
成对组合覆盖这一概念是Mandl于1985年在测试Aad编译程序时提出来的.Cohen等人应用成对组合覆盖测试技术对Unix中的“Sort”命令进行了测试.测试结果表明覆盖率高达90%以上.可见成对组 ...
- springboot整合jersey
https://blog.csdn.net/xiongpei00/article/details/76576420
- Python全栈工程师(while、占位符)
ParisGabriel Python 入门基础 UnicodeASCII 用8个位表示文字 ,最高位一定是零,低七位表示数值Unicode是由16个位组成的(65535) 最 ...
- 孤荷凌寒自学python第五十四天使用python来删除Firebase数据库中的文档
孤荷凌寒自学python第五十四天使用python来删除Firebase数据库中的文档 (完整学习过程屏幕记录视频地址在文末) 今天继续研究Firebase数据库,利用google免费提供的这个数据库 ...
- Visual C++ 图像处理类库CxImage源代码
说明:VC++ 图像处理类库CxImage源代码,CxImage是一个可以用于MFC 的C++类,可以打开,保存,显示,转换各种格式的图像文件,比如BMP, JPEG, GIF, PNG, TIFF, ...
- Qt Creator : Read an image from resources
最近两周碰到的一个问题是: opencv无法读取qt中的资源文件. 参考网址:https://stackoverflow.com/questions/45233559/qt-creator-read- ...
- iOS runLoop 理解
目录 概述 run loop modes 一.概述 run loop叫事件处理循环,就是循环地接受各种各样的事件.run loop是oc用来管理线程里异步事件的工具.一个线程通过run loop可以监 ...
- html & email template
html & email template inline style build tools https://templates.mailchimp.com/getting-started/h ...
- Nginx负载均衡的实现(初级)
不用nginx.conf,新建一个 fzjh.conf (名称自定义) 内容如下: user nobody; # 声明用户为nobody worker_processes 4; # 开启的nginx ...
- 【bzoj1179】[Apio2009]Atm Tarjan缩点+Spfa最长路
题目描述 输入 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每 ...