CSP-S 32 && 多校5
10.15 S-32&&多校5
签到后遗憾离场。
t1
签到题,大力分讨即可。
注意符号。
记得排序(没大阳历时忘排序了)
code
锵锵
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int T, m, n;
struct node
{
int tim, val;
} a[N];
inline bool cmp(node a, node b)
{
return a.tim < b.tim;
}
signed main()
{
freopen("count.in", "r", stdin);
freopen("count.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> T;
while (T--)
{
cin >> m >> n;
bool flag = 1;
int lastim = 0, lasval = 0;
for (int i = 1; i <= n; ++i)
cin >> a[i].tim >> a[i].val;
sort(a + 1, a + 1 + n, cmp);
for (int i = 1; i <= n; ++i)
{
int tim = a[i].tim, val = a[i].val;
if (!flag)
continue;
if (!val)
{
lastim = tim, lasval = val;
continue;
}
if (!lasval)
{
if (abs(val) > tim - lastim)
flag = 0;
lastim = tim, lasval = val;
continue;
}
if ((val > 0 && lasval > 0) || (val < 0 && lasval < 0))
{
if (tim - lastim >= abs(val) + 1) // 0
{
lastim = tim, lasval = val;
continue;
}
if (abs(val - lasval) > tim - lastim)
{
flag = 0;
continue;
}
if ((tim - lastim - abs(val - lasval)) % 2 == 0)
lastim = tim, lasval = val;
else
flag = 0;
}
else
{
if (tim - lastim >= abs(val) + 1) // 0
lastim = tim, lasval = val;
else
flag = 0;
}
}
if (flag)
cout << "Yes\n";
else
cout << "No\n";
}
return 0;
}
t2
若读假题面(谁这么唐我不说),将积看成和,则对于每个贡献值计算出其出现次数即可(易得这个次数为 2 的幂)。
可惜题面为积 \(\ldots\)
先将答案表达式拆开,将积的平方拆为平方的积,之后便满足分配律。
考虑 \(dp_i\) 为 \(1\) 到 \(i\) 的答案,则终态为 \(dp_n\) .
记 \(s\) 数组为异或前缀和,易得转移方程式:
\(dp_i=\sum_{j=0}^{i-1}dp_j*(s_i\oplus s_j)^2\)
这步其实是好推的,但赛时被读假题面后的思路局限住了(太唐了)。
考虑如何做到正解:
发现平方和这个东西和异或在一起根本没法计算,于是将异或拆位,并先忽略平方。
令 \(r_i\) 为 \(r\) 二进制下第 \(i\) 位。
具体的,两数 \(x,y\) 异或为:
\(ans=\sum_{i=0}^{len}2^k[x_i=y_i]\)
这里 len 为二进制表示下的长度。
于是 dp 式子可化为:
\(dp_i=\sum_{j=0}^{i-1}dp_j\sum_{k=0}^{len}2^k[s(i)_k=s(j)_k]\)
即:
\(dp_i=\sum_{k=0}^{len}2^k\sum_{j=0}^{i-1}dp_j[s(i)_k=s(j)_k]\)
发现对于后一个 \(\sum\) 中的东西是一个类似前缀和的形式且 \([s(i)_k=s(j)_k]\) 中 \(s_i\) 不变,启发我们进行前缀和优化。
具体的,记 \(sum_{i,0/1}\) 表示二进制下第 \(i\) 位为 \(0/1\) 的 \(dp\) 值的和。
\(dp_i=\sum_{k=0}^{len}2^ksum_{k,s(i)_k\oplus 1}\)
\(sum_{k,i}=\sum_{k=0}^{len}dp_i[s(i)_k\oplus 1=i]\)
此时时间复杂度为 \(O(n\log_n)\),可过。
but \(\ldots\)
我们刚才忽略了平方,那么加上平方后怎么做呢?
加上平方后,唯一难处理的项还是异或。
此时要对 \(\sum_{k=0}^{len}2^k[s(i)_k=s(j)_k]\) 进行平方。
考虑 \(\sum_{i=1}^nc_i\) 进行平方,展开为 \(\sum_{i=1}^n\sum_{j=1}^nc_ic_j\),类似的,\(\sum_{k=0}^{len}2^k[s(i)_k=s(j)_k]\) 即为 \(\sum_{k1=0}^{len}\sum_{k2=0}^{len}2^{k1}2^{k2}[s(i)_{k1}=s(j)_{k1} \land s(i)_{k2}=s(j)_{k2}]\)
于是转移方程为:
\(dp_i=\sum_{k1=0}^{len}\sum_{k2=0}^{len}2^{k1}2^{k2}[s(i)_{k1}=s(j)_{k1} \land s(i)_{k2}=s(j)_{k2}]\sum_{j=0}^{i-1}dp_j\)
同上,我们记 \(sum_{i,j,0/1,0/1}\),表示二进制下第 \(i\) 位为 \(0/1\) ,第 \(j\) 位为 \(0/1\) 的 \(dp\) 值的和。
之后同理转移即可。
记得赋初值!!!
code
噔噔
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
int T, n;
int a[N], dp[N], s[N];
int sum[33][33][2][2], lw[70];
signed main()
{
freopen("partition.in", "r", stdin);
freopen("partition.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
lw[0] = 1;
for (int i = 1; i < 70; ++i)
lw[i] = lw[i - 1] * 2 % mod;
cin >> T;
while (T--)
{
memset(dp, 0, sizeof(dp));
memset(sum, 0, sizeof(sum));
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
for (int i = 1; i <= n; ++i)
s[i] = s[i - 1] ^ a[i];
dp[0] = 1;
for (int k1 = 0; k1 <= 32; ++k1)
for (int k2 = 0; k2 <= 32; ++k2)
sum[k1][k2][0][0] += 1;
for (int i = 1; i <= n; ++i)
{
int num = 0;
for (int k1 = 0; k1 <= 32; ++k1)
for (int k2 = 0; k2 <= 32; ++k2)
{
num += lw[k1 + k2] * sum[k1][k2][!((s[i] >> k1) & 1)][!((s[i] >> k2) & 1)] % mod;
num %= mod;
}
// cerr << "num=" << num << "\n";
dp[i] = num;
for (int k1 = 0; k1 <= 32; ++k1)
for (int k2 = 0; k2 <= 32; ++k2)
{
sum[k1][k2][(s[i] >> k1) & 1][(s[i] >> k2) & 1] += dp[i];
sum[k1][k2][(s[i] >> k1) & 1][(s[i] >> k2) & 1] %= mod;
}
}
cout << dp[n] << "\n";
}
return 0;
}
t3
要求极差最小,我们可以考虑枚举最小值,找到其对应的最大值的最小值,作差后取最小即可。
如果太多最大值最小值容易弄混,可以将其均转化成含 \(\min\) 与 \(\max\) 的公式。
考虑最小值种类不同的个数是 \(O(n)\) 级别的(至多有 \(2n-1\) 个)
因此对于每次查找最大值要求在 \(O(log_n)\) 的复杂度左右求出。
区间问题,\(log\) 级别复杂度 \(\ldots\)
就是线段树!
但是如何维护信息呢?
题目中有一个重要限制:每组包含恰好一个任务,或恰好两个相邻的任务
对于一个任务,自然就是叶子结点本身维护的一个值,而对于恰好两个相邻的任务,考虑在每个叶子结点上维护 \(dp_{0/1,0/1}\) 。
\(dp_{0,0}\) 表示该结点所表示区间中分组均在区间内;
\(dp_{1,0}\) 表示该结点所表示区间中分组左侧超出区间;
\(dp_{0,1}\) 表示该结点所表示区间中分组右侧超出区间;
\(dp_{1,1}\) 表示该结点所表示区间中分组左,右均侧超出区间;
对于 pushup 函数按照定义转移即可。
pushup
inline void pushup(int id)
{
dp[id][0][0] = min(max(dp[lid][0][0], dp[rid][0][0]), max(dp[lid][0][1], dp[rid][1][0]));
dp[id][0][1] = min(max(dp[lid][0][0], dp[rid][0][1]), max(dp[lid][0][1], dp[rid][1][1]));
dp[id][1][0] = min(max(dp[lid][1][0], dp[rid][0][0]), max(dp[lid][1][1], dp[rid][1][0]));
dp[id][1][1] = min(max(dp[lid][1][0], dp[rid][0][1]), max(dp[lid][1][1], dp[rid][1][1]));
}
如此操作,\(dp_{1,0,0}\) 即为最大值的最小值 。
我们将所有可能的最小值去重后按升序排序,依次取出,统计答案后在线段树上去除参与构成当前最小值的点产生的贡献,即将对应点赋值为 inf 。
注意操作细节,特别注意边界。
code
哒哒哒
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
const int inf = 1e18;
int T, n, cnt;
int a[N], b[N], c[N];
unordered_map<int, vector< pair<int, int> > > mp;
int dp[N << 2][2][2];
struct node
{
int l, r;
} t[N << 2];
#define lid (id << 1)
#define rid (id << 1 | 1)
inline void pushup(int id)
{
dp[id][0][0] = min(max(dp[lid][0][0], dp[rid][0][0]), max(dp[lid][0][1], dp[rid][1][0]));
dp[id][0][1] = min(max(dp[lid][0][0], dp[rid][0][1]), max(dp[lid][0][1], dp[rid][1][1]));
dp[id][1][0] = min(max(dp[lid][1][0], dp[rid][0][0]), max(dp[lid][1][1], dp[rid][1][0]));
dp[id][1][1] = min(max(dp[lid][1][0], dp[rid][0][1]), max(dp[lid][1][1], dp[rid][1][1]));
}
inline void build(int id, int l, int r)
{
t[id].l = l, t[id].r = r;
if (l == r)
{
dp[id][0][0] = a[l];
dp[id][1][0] = a[l] + a[l - 1];
dp[id][0][1] = a[l] + a[l + 1];
dp[id][1][1] = inf;
return;
}
int mid = (l + r) >> 1;
build(lid, l, mid);
build(rid, mid + 1, r);
pushup(id);
}
inline void update(int id, int pos, int opt)
{
if (t[id].l == t[id].r && t[id].l == pos)
{
// if (!opt)
if (a[pos] <= opt)
dp[id][0][0] = inf;
// else if (opt == 1)
if (pos == 1 || (pos > 1 && a[pos - 1] + a[pos] <= opt))
dp[id][1][0] = inf;
// else
if (pos == n || (pos < n && a[pos + 1] + a[pos] <= opt))
dp[id][0][1] = inf;
return;
}
int mid = (t[id].l + t[id].r) >> 1;
if (mid >= pos)
update(lid, pos, opt);
else
update(rid, pos, opt);
pushup(id);
}
void Deb(int l, int r, int p)
{
cout << "[" << l << "," << r << "]" << " p=" << p << "\n";
cout << "dp[p][0][0]=" << dp[p][0][0] << "\n";
cout << "dp[p][0][1]=" << dp[p][0][1] << "\n";
cout << "dp[p][1][0]=" << dp[p][1][0] << "\n";
cout << "dp[p][1][1]=" << dp[p][1][1] << "\n\n";
int mid = (l + r) >> 1;
if (l == r)
return;
Deb(l, mid, p << 1);
Deb(mid + 1, r, p << 1 | 1);
}
signed main()
{
freopen("opti.in", "r", stdin);
freopen("opti.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> T;
while (T--)
{
mp.clear();
cnt = 0;
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
for (int i = 1; i < n; ++i)
b[i] = a[i] + a[i + 1];
for (int i = 1; i < n; ++i)
{
c[++cnt] = a[i], c[++cnt] = b[i];
mp[a[i]].push_back({i, 0});
mp[b[i]].push_back({i, i + 1});
}
mp[a[n]].push_back({n, 0});
c[++cnt] = a[n];
a[0] = a[n + 1] = inf;
sort(c + 1, c + 1 + cnt);
cnt = unique(c + 1, c + 1 + cnt) - c - 1;
build(1, 1, n);
int ans = inf;
// for(int i=1;i<=cnt;i++) cout<<c[i]<<" ";
// cout<<"\n";
for (int i = 1; i <= cnt; ++i)
{
if(dp[1][0][0]>=inf) break;
ans = min(ans, dp[1][0][0] - c[i]);
// cout << "mx=" << dp[1][0][0] <<" "<<c[i]<< "\n";
// Deb(1,n,1);
// cerr<<"siz="<<mp[c[i]].size()<<"\n";
for (auto y : mp[c[i]])
{
if (!y.second)
update(1, y.first, c[i]);
else
update(1, y.first, c[i]), update(1, y.second, c[i]);
}
// cout<<"\n------------------------------\n\n\n";
}
cout << ans << "\n";
}
return 0;
}
t4
咕咕嘎嘎
本文来自博客园,作者:HS_fu3,转载请注明原文链接:https://www.cnblogs.com/HS-fu3/p/19153757

浙公网安备 33010602011771号