25南昌IC邀请赛vp补题
博客内容是从易到难(自我认为)
A Nezha Naohai
知识点:无

签到,显而易见,把三个数加起来×第四个数就结束了。
M Divide coins
知识点:构造

这个题是个构造,自己手模几种情况,很容易发现,如果有k个要反转的,假设n个数左边有x个反转的,右边有k-x个反转的,那么发现如果左边是k个,右边是n-k个数,让左边不反转直接放入左边(1操作),右边反转后放进右边(4操作), 左右正面的数量一定是一样的,故而可得。
void solve()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++)
cout << 1;
for (int i = m + 1; i <= n; i++)
cout << 4;
}
K Rotation
知识点:正难则反,思维

正难则反,把一个鼻子固定不动,其他全动,就相当于其他不动,所以枚举每个雕像转到每一种方向的代价,然后最后在分别加上通过第二种操作转到正向的代价,取最小值就行了。
void solve()
{
cin >> n;
vi a(n + 1);
int ans = 1e18;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int j = 0; j < 4; j++)
{
int now = 0;
for (int i = 1; i <= n; i++)
{
int d = (a[i] + 4 - j) % 4;
now += d;
}
now += (4 - (now + j) % 4) % 4;
ans = min(ans, now);
}
cout << ans << endl;
}
D Virtuous Pope
知识点:差分,离散化

因为平面是有特点的,一定是和三个坐标轴其中之一垂直的,所以我们可以分别计算三个方向的数目,存进数组用离散化即可。
(注意0-based还是1-based)
void solve()
{
int n, a, b, c;
cin >> n >> a >> b >> c;
struct node
{
int x, y, z;
node() {}
node(int x, int y, int z) : x(x), y(y), z(z) {}
};
vector<int> bokx, boky, bokz;
vector<node> be(n + 7), ed(n + 7);
for (int i = 1; i <= n; i++)
{
int x, y, z;
cin >> x >> y >> z;
be[i] = node(x, y, z);
bokx.push_back(x), boky.push_back(y), bokz.push_back(z);
cin >> x >> y >> z;
ed[i] = node(x, y, z);
bokx.push_back(x), boky.push_back(y), bokz.push_back(z);
}
sort(all(bokx)), sort(all(boky)), sort(all(bokz));
bokx.erase(unique(all(bokx)), bokx.end());
boky.erase(unique(all(boky)), boky.end());
bokz.erase(unique(all(bokz)), bokz.end());
for (int i = 1; i <= n; i++)
{
be[i].x = lower_bound(all(bokx), be[i].x) - bokx.begin() + 1;
be[i].y = lower_bound(all(boky), be[i].y) - boky.begin() + 1;
be[i].z = lower_bound(all(bokz), be[i].z) - bokz.begin() + 1;
ed[i].x = lower_bound(all(bokx), ed[i].x) - bokx.begin() + 1;
ed[i].y = lower_bound(all(boky), ed[i].y) - boky.begin() + 1;
ed[i].z = lower_bound(all(bokz), ed[i].z) - bokz.begin() + 1;
}
int ans = 0;
vector<int> cx(n + 7);
for (int i = 1; i <= n; i++)
{
int bex = min(be[i].x, ed[i].x), edx = max(be[i].x, ed[i].x);
cx[bex]++, cx[edx + 1]--;
}
for (int i = 1; i <= n; i++)
{
cx[i] += cx[i - 1];
ans = max(ans, cx[i]);
}
vector<int> cy(n + 7);
for (int i = 1; i <= n; i++)
{
int bey = min(be[i].y, ed[i].y), edy = max(be[i].y, ed[i].y);
cy[bey]++, cy[edy + 1]--;
}
for (int i = 1; i <= n; i++)
{
cy[i] += cy[i - 1];
ans = max(ans, cy[i]);
}
vector<int> cz(n + 7);
for (int i = 1; i <= n; i++)
{
int bez = min(be[i].z, ed[i].z), edz = max(be[i].z, ed[i].z);
cz[bez]++, cz[edz + 1]--;
}
for (int i = 1; i <= n; i++)
{
cz[i] += cz[i - 1];
ans = max(ans, cz[i]);
}
cout << ans << '\n';
}
F Caloric Difference
知识点:贪心


解析这个公式,p是给出的,也就是表明下一个ci一部分是ci-1,一部分是ri,那么计算ci+1+ci,然后把公式代入发现,ri越小越好。
观察题目,给了部分ri,然后给了ri的范围【l,r】,所以就很好处理了,如果知道ri的值,直接推公式就行,如果不知道,就可以设置为最小值 l ,这样得出来一定是最大的。
void solve()
{
int n, k;
cin >> n >> k;
vector<double> r(n + 7), c(n + 7);
double p, L, R;
cin >> r[0] >> c[0] >> p >> L >> R;
for (int i = 1; i <= k; i++)
{
int pos;
cin >> pos;
cin >> r[pos];
}
for (int i = 1; i <= n; i++)
{
if (r[i] == 0)
r[i] = L;
c[i] = p * c[i - 1] + (1.0 - p) * r[i - 1];
}
double ans = 0;
for (int i = 1; i <= n; i++)
ans += c[i] - r[i];
cout << fixed << setprecision(10) << ans << '\n';
}
G Exploration
知识点:DP


发现如果对每个点贪心的话,显然不对,这个时候我们就可以考虑考虑dp,
如果考虑每个点的除法的话,时间复杂度不小。又因为向下取整有个小结论,x/a/b=x/a*b。
故而我们维护每个点走j步要消耗的代价乘积,然后询问时只需要遍历这个步数就可以了,因为步数很小,最多就30多次,所以时间复杂度完全可以接受。
因为这个dp不是线性的,所以考虑是否有多个维护值,dp[i]代表第i个点,很容易发现发现用dp[i][j]表示第j步以i为起点所消耗的代价乘积。
struct node
{
int to, w;
};
vector<node> adj[M];
void solve()
{
int q;
cin >> n >> m >> q;
for (int i = 1; i <= m; i++)
{
int u, v, d;
cin >> u >> v >> d;
adj[u].push_back({v, d});
}
vvi dp(n + 1, vi(32, 0));
for (int i = 1; i <= n; i++)
dp[i][0] = 1;
for (int p = 1; p < 32; p++)
{
for (int i = 1; i <= n; i++)
{
for (auto j : adj[i])
{
int num = dp[j.to][p - 1] * j.w;
num = min(num, INF);
dp[i][p] = max(dp[i][p], num);
}
}
}
while (q--)
{
int p;
cin >> p >> x;
for (int i = 1; i < 32; i++)
{
if (dp[p][i] > x)
{
cout << i << endl;
break;
}
}
}
}
I Dating Day
知识点:组合数学,双指针,容斥原理
(这个题说明了翻译的重要性())

因为是方案问题,所以肯定有多种操作方法,发现一个连续子段,只要有k个约会,就满足条件了,然后对这个子段进行操作,发现如果这个子段长度越大,这个子段贡献的方案数越多,
而且,你对大子段进行操作,他的方案数一定包括你对小子段的操作吧!所以我们想要一个有且仅有k个约会的超大子段。
但是呢,我们也不难发现,这种超大子段一定会相交吧,所以呢,我们还要用容斥原理去除相交的地方才行。
如果想要最大的方案数,一定是从左端开始找,找到一个就计算贡献C(R-L+1,K),然后接着慢慢往后找,发现中间重合的部分是新的 L2 到旧的 R1,然后很容易发现这里面有k-1个约会,所以重复计算的是这个C(R1-L2+1,K-1),减去这个贡献,接着找。
你也发现了,这就是双指针()加一点容斥原理,再用一点组合数学,好像还真的不难的样子(?
int f[M], ivf[M];
void init()
{
f[0] = 1;
for (int i = 1; i < M; i++)
{
f[i] = f[i - 1] * i % mod;
}
ivf[M - 1] = ksm(f[M - 1], mod - 2, mod);
for (int i = M - 2; i >= 0; i--)
{
ivf[i] = ivf[i + 1] * (i + 1) % mod;
}
}
int C(int a, int b)
{
return f[a] * ivf[a - b] % mod * ivf[b] % mod;
}
void solve()
{
cin >> n >> k >> s;
s = '#' + s;
int l = 1, r = 0;
int now = 0;
while (r <= n && now <= k)
{
r++;
now += (s[r] == '1');
}
now -= (s[r] == '1');
r--;
if (now != k)
{
cout << 0 << endl;
return;
}
int ans = 0;
ans += C(r - l + 1, k);
while (l <= r && r <= n)
{
int temp = r;
while (l <= r && now == k)
{
now -= (s[l] == '1');
l++;
}
while (r <= n && now <= k)
{
r++;
now += (s[r] == '1');
}
now -= (s[r] == '1');
r--;
if (now != k || r < l)
{
break;
}
ans = (ans + C(r - l + 1, k)) % mod;
ans = ((ans - C(temp - l + 1, k - 1)) % mod + mod) % mod;
}
cout << ans << endl;
}
E God's String on This Wonderful World
知识点:莫队,哈希
虽然是普通莫队,但是是哈希+莫队(),有些细节处理不好就会爆炸()。


题意是询问你在给定的范围中 有多少子串中26个字母的个数都能整除k。
显然是要预处理一下的,用c数组来计算前缀26个字母的个数,然后我们发现,如果有子串满足那个条件的话,c[r]和c[l]是相等的(mod k),我们未尝不能用这个特点来预处理,计算c数组的种类编号,遇到新的c数组,编号++,两个相等编号的c数组,就是想要的子串吧。
然后用map维护编号,同时用另一个数组记录每一个位置的编号。预处理询问数组,分块,将这个数组的l,r排序,用莫队维护哈希值的数量,用莫队的经典操作来求结果就行了(至于奇偶优化,看个人了),
vi c;
map<vi, int> p;
int ans[M];
int id[M];
int now = 0;
struct node
{
int li, ri, hi;
} que[M];
int len;
int sum[M];
bool cmp(node a, node b)
{
if (a.li / len != b.li / len)
{
return a.li < b.li;
}
return a.ri < b.ri;
}
int res;
void add(int x)
{
res += sum[x];
sum[x]++;
}
void del(int x)
{
sum[x]--;
res -= sum[x];
}
void solve()
{
int q;
cin >> n >> k >> q;
len = sqrt(n);
cin >> s;
s = '#' + s;
c.resize(26, 0);
p[c] = ++now;
id[0] = now;
for (int i = 1; i <= q; i++)
{
cin >> que[i].li >> que[i].ri;
que[i].li--;
que[i].hi = i;
}
sort(que + 1, que + q + 1, cmp);
for (int i = 1; i <= n; i++)
{
c[s[i] - 'a']++;
c[s[i] - 'a'] %= k;
if (!p[c])
{
p[c] = ++now;
}
id[i] = p[c];
}
int l = 1, r = 0;
for (int i = 1; i <= q; i++)
{
while (l > que[i].li)
{
add(id[--l]);
}
while (r < que[i].ri)
{
add(id[++r]);
}
while (l < que[i].li)
{
del(id[l++]);
}
while (r > que[i].ri)
{
del(id[r--]);
}
ans[que[i].hi] = res;
}
for (int i = 1; i <= q; i++)
{
cout << ans[i] << endl;
}
}
先补到这里()

浙公网安备 33010602011771号