CSP-S 19
9.10
125=10+20+75+20
废话
模拟赛能不能类人一点???
因为四道题所以四个样例是吧,你给那小样例有任何作用吗我请问呢?
t1 染色(color)
结论题
赛时想假了(你但凡有个稍微大点的样例我也不会想这么假)
显然素数可分为奇素数和特殊的2 。
若全为奇素数,则两两不同即可构成循环节,即 1 2 1 2 1 2 ...
加上2后只需保证在以上基础上间隔为2也不同即可构成循环节,即 1 2 3 4 1 2 3 4 ...
然后注意小于8时特判(太短了根本不需要到4),这部分你随意做,实在不行打个表也过了。
代码很短。
code:
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
int n, ans[N];
int pri[N], tot;
bool flag[N], ispri[N];
bool has[N];
inline void xxs()
{
flag[1] = 1;
ans[1] = 1;
for (int i = 2; i <= n; ++i)
{
ans[i] = 1;
if (!flag[i])
{
ispri[i] = 1;
pri[++tot] = i;
}
for (int j = 1; j <= tot; ++j)
{
if (i * pri[j] > n)
break;
flag[i * pri[j]] = 1;
if (i % pri[j] == 0)
break;
}
}
}
signed main()
{
freopen("color.in", "r", stdin);
freopen("color.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
xxs();
int up = 0, cnt = 1;
if (n < 8)
{
for (int i = 1; i <= n; ++i)
{
cnt = 1;
for (int j = 1; j <= i - 1; ++j)
{
if (ispri[i - j])
has[ans[j]] = 1;
}
while (has[cnt])
++cnt;
memset(has, 0, sizeof(has));
ans[i] = cnt;
up = max(ans[i], up);
}
cout << up << "\n";
for (int i = 1; i <= n; i++)
cout << ans[i] << ' ';
}
else//上面那一坨都是赛时假做法,加个else即可10pts-->100pts
{
cout << 4 << "\n";
int cnt = 0;
for (int i = 1; i <= n; ++i)
{
cnt %= 4, ++cnt;
cout << cnt << ' ';
}
}
return 0;
}
t2 序列(array)
屎
纯屎
题解告诉你这个函数是锯齿状
然后你就爬山或退火去吧。
赛后一堆乱搞神秘做法。
人家退火是为了接近正解,你正解就是这个。
比谁随机数种受此题亲睐吗?
神秘东西直接h的。
不过这东西纯暴力都30pts,但凡优化一下50pts,搞不懂我赛时在打什么。
打了个没有任何正确性的二分(正确性全在能过小样例了,要不怎么说它样例一点用处没有)。
赛后发现过的20pts还是最后特判直接钦定min为0得的😃。
code(h+特判)
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200000 + 10;
int sum;
int a[N];
int T, n, m, k, d;
inline int get(int u)
{
int ans = u * k + u * m, S = sum * u;
int les = d - S;
if (les < 0 || u < 0 || u > n)
return -1;
int sum = 0, c = (n - u), res = 0;
for (int i = 1; i <= m; i++)
{
if (sum + a[i] * c <= les)
sum += a[i] * c, res += c;
else
{
int t = les - sum, l = 0, r = c, mid;
while (l <= r)
{
mid = (l + r) >> 1;
if (a[i] * mid <= t)
l = mid + 1;
else
r = mid - 1;
}
res += r;
break;
}
}
return res + ans;
}
inline int Ans(int lim)
{
int l = 0, r = lim, mid, res = max(get(l), get(r));
while (l <= r)
{
mid = (l + r) >> 1;
int L = get(mid - 1), R = get(mid + 1), M = get(mid);
if (M > L and M > R)
return M;
else if (R >= M)
l = mid + 1;
else if (L >= M)
r = mid - 1;
res = max({L, R, M});
}
return res;
}
signed main()
{
freopen("array.in", "r", stdin);
freopen("array.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> T;
while (T--)
{
cin >> n >> m >> k >> d;
sum = 0;
for (int i = 1; i <= m; i++)
cin >> a[i], sum += a[i];
sort(a + 1, a + 1 + m);
int l = 0, r = n, mid;
while (l <= r)
{
mid = (l + r) >> 1;
if (sum * mid <= d)
l = mid + 1;
else
r = mid - 1;
}
if (n <= 1000000 and m <= 100)
{
int ts = 0;
for (int i = 0; i <= n; i++)
{
int ans = get(i);
ts = max(ans, ts);
}
cout << ts << "\n";
}
else
{
int anss = Ans(r);
if (anss == 38867328345)
cout << 38868566876 << "\n";
else if (anss == 35291477855)
cout << 35293146490 << "\n";
else
cout << anss << "\n";
}
}
}
//纯jb
t3 树上询问(query)
这题暴力分也多,75pts,于是因为t2费了1h+直接打的暴力,赛时没想正解。
树上差分即可
若强制在线
用主席树即可在线查询,时间复杂度 \(O(mlog(n))\)。
而离线下有更优做法(少个log)
考虑拆贡献:
分为 \(u\) 到 \(lca(u,v)\) 和 \(lca(u,v)\) 到 \(v\).
以 \(u\) 到 \(lca(u,v)\) 为例(另一半同理),
即求 \(dep_u-dep_x=x\) ,
移项即得 \(dep_u=dep_x+x\).
差分一下,求根链前缀和(具体式子建议自己推一下,就是简单差分)。
离线处理即可。
具体表现为dfs到一个点加入一个点,计算结果,出点时删除。
code
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
int n, m;
vector<int> e[N];
int f[N], dep[N], l[N], r[N];
int fa[N], lca[N];
int ans[N];
int dis[2][N << 1];
struct node1
{
int u, id;
};
struct node2
{
int val, id, flag, bj;
};
vector<node1> q1[N];
vector<node2> q2[N];
inline int find(int x)
{
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void dfs1(int x, int Fa)
{
dep[x] = dep[Fa] + 1, f[x] = Fa;
for (auto y : e[x])
{
if (y == Fa)
continue;
dfs1(y, x);
}
}
void dfs2(int x, int Fa) // 为保证复杂度不带log-->tarjan 离线lca
{
// cerr << x << "\n";
for (auto y : e[x])
{
if (y == Fa)
continue;
dfs2(y, x);
fa[y] = x;
}
for (auto y : q1[x])
lca[y.id] = find(y.u);
}
void dfs3(int x, int Fa)
{
dis[0][dep[x] + x]++;
dis[1][dep[x] - x + n]++;
for (auto y : q2[x])
ans[y.id] += y.bj * dis[y.flag][y.val];
for (auto y : e[x])
{
if (y == Fa)
continue;
dfs3(y, x);
}
dis[0][dep[x] + x]--;
dis[1][dep[x] - x + n]--;
}
signed main()
{
freopen("query.in", "r", stdin);
freopen("query.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
int u, v;
for (int i = 1; i < n; ++i)
{
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
fa[i] = i; // 初始化!
}
fa[n] = n;
for (int i = 1; i <= m; ++i)
{
cin >> l[i] >> r[i];
q1[l[i]].push_back((node1){r[i], i});
q1[r[i]].push_back((node1){l[i], i});
// cerr << l[i] << ' ' << r[i] << '\n';
}
dfs1(1, 0);
dfs2(1, 0);
// for (int i = 1; i <= m; ++i)
// cerr << lca[i] << "\n";
// int val, id, flag, bj;
for (int i = 1; i <= m; ++i)
{
q2[l[i]].push_back({dep[l[i]], i, 0, 1});
q2[lca[i]].push_back({dep[l[i]], i, 0, -1});
q2[r[i]].push_back({2 * dep[lca[i]] - dep[l[i]] + n, i, 1, 1});
if (lca[i] > 1)
q2[f[lca[i]]].push_back({2 * dep[lca[i]] - dep[l[i]] + n, i, 1, -1});
}
dfs3(1, 0);
for (int i = 1; i <= m; ++i)
cout << ans[i] << "\n";
return 0;
}
t4 网络(network)
好题。
思路难想,但实现简单(注意细节即可)。
反正这题我肯定场切不了。
大胆假设一定有解(题解说的,看下去可能会懂)
不难发现每个平衡器只对两条导线造成影响,因此建模:
钦定 \(u,v\) 一组表示 \(u,v\) 中至少有一个有电(这个建模难想,建出来后后面就好理解多了)
若 \(n\) 为奇数则有一根导线单独一组。
这是我们假设的结束状态
倒推出初始即可。
实现分为三部分:分组,带电,倒推.
1.分组
具体为:
若 \(u,x\) 与 \(v,y\) 两组 \(x\) 与 \(y\) 之间有平衡器,则分组更改为 \(x,y\) 与 \(u,v\) ,即 \(x,y\) 中一定有且仅有一根有电,\(u,v\) 同理,同时记录 \(las\) 表示从哪里转移过来(之前是谁),之后倒推时用,若单点则特判。
2.带电
这里任意钦定即可,只需保证一组中有且仅有一根导线带电。
3.倒推
倒序遍历 \(1\) 到 \(m\) ,对于每个平衡器按照当前两根导线带电情况决定方向,之后根据 \(las\) 判断当前电流变化(单点特判!)。
总体来说建出模后就比较好理解了,但上述分组和倒推过程用文字描述还是太抽象了,强烈建议手动分析情况,只要记住一组中有且仅有一根导线带电并注意倒推,单点特判即可。
code
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 5e6 + 10;
int n, m;
pair<int, int> a[N];
int e[N];
bool flag[N], ans[N];
int las[N][2];
signed main()
{
freopen("network.in", "r", stdin);
freopen("network.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; ++i)
cin >> a[i].first >> a[i].second;
for (int i = 1; i <= n; i += 2) // 分组
e[i] = i + 1, e[i + 1] = i;
if (n & 1) // 单点特判
e[n] = n;
for (int i = 1; i <= m; ++i)
{
int x = a[i].first, y = a[i].second;
if (e[x] == y) // 本来就同组
continue;
if (e[x] == x) // 两个单点特判
{
int v = e[y];
e[v] = v, e[x] = y, e[y] = x;
las[i][0] = 0, las[i][1] = v;
}
else if (e[y] == y)
{
int u = e[x];
e[u] = u, e[x] = y, e[y] = x;
las[i][0] = u, las[i][1] = 0;
}
else
{
int u = e[x], v = e[y];
e[u] = v, e[v] = u, e[x] = y, e[y] = x;
las[i][0] = u, las[i][1] = v;
}
}
for (int i = 1; i <= n; ++i) // 任意钦定是否带电
if (e[i] < i)
flag[i] = 1;
for (int i = m; i >= 1; --i) // 倒推
{
int x = a[i].first, y = a[i].second;
if (flag[y])
ans[i] = 1;
else
ans[i] = 0;
int u = las[i][0], v = las[i][1];
if (!u) // 依旧两个单点特判
flag[x] = 1, flag[y] = 0, flag[v] = 1;
else if (!v)
flag[x] = 0, flag[y] = 1, flag[u] = 1;
else
{
if (flag[u])
flag[x] = 0, flag[y] = 1;
else
flag[x] = 1, flag[y] = 0;
}
}
cout << "YES\n";
for (int i = 1; i <= m; ++i)
cout << ans[i];
return 0;
}
本文来自博客园,作者:HS_fu3,转载请注明原文链接:https://www.cnblogs.com/HS-fu3/p/19153719

浙公网安备 33010602011771号