The 2023 ICPC Asia Nanjing Regional Contest (The 2nd Universal Cup. Stage 11: Nanjing)(SDKD 2024 Summer Training Contest G1)
C - Primitive Root
题意
给定p与m(p为质数),已知(g ^ (P - 1)) % P == 1且g <= m。求g的个数。
思路
由(g ^ (P - 1)) % P == 1与异或性质a - b <= a ^ b <= a + b,可以推出g = ((k * p + 1) ^ (p - 1))与p * (k - 1) + 2 <= g <= p * (k + 1)。又因为g <= m,则当p * (k + 1) <= m,一定合法;p * (k - 1) + 2 > m一定不合法;二者之间单独判断。
原根(虽然没什么用):https://zhuanlan.zhihu.com/p/591377528
代码
点击查看代码
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mxn = 1e6 + 5;
void solve()
{
int m, p, ans = 0;
scanf("%lld%lld", &p, &m);
// (k < m \ p - 1)一定满足(加上0共n / p个),(k > (m - 2) / p + 1)一定不满足
int minn = m / p, maxn = (m - 2) / p + 1;
if ((m - 2) % p) // 考虑取整
{
maxn++;
}
for (int k = minn; k <= maxn; k++) // 二者之间单独判断,数据量很小
{
int g = ((k * p + 1) ^ (p - 1));
if (g <= m)
{
ans++;
}
}
printf("%lld\n", m / p + ans);
}
signed main()
{
int T = 1;
scanf("%lld", &T);
while (T--)
{
solve();
}
return 0;
}
F - Equivalent Rewriting
题意
长度为m的序列全初始化为0,n次操作,第i次操作把给出的k个位置的值换成i。问重新排列操作能否得到原来序列。
思路
对于每个位置,按序进行操作\({a_1, a_2,···, a_k}\),则最后的值只能是\(a_k\)。考虑各操作之间有先后的关系,可以把每次操作看作点,所建图的任意拓扑序即为答案。
代码
点击查看代码
#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mxn = 1e6 + 5;
int n, m, idx, cnt;
int to[mxn], nxt[mxn], head[mxn], indegree[mxn], ans[mxn];
vector<vector<int>> p;
void init()
{
fill(head, head + n + 1, 0);
fill(indegree, indegree + n + 1, 0);
p.clear();
p.resize(m + 1);
idx = cnt = 0;
}
void add(int u, int v)
{
to[++idx] = v;
nxt[idx] = head[u];
head[u] = idx;
}
void toposort()
{
priority_queue<int> q;
for (int i = 1; i <= n; i++)
{
if (!indegree[i])
{
q.push(i);
}
}
while (q.size())
{
int u = q.top();
q.pop();
ans[++cnt] = u;
for (int i = head[u]; i; i = nxt[i])
{
int v = to[i];
indegree[v]--;
if (!indegree[v])
{
q.push(v);
}
}
}
}
void solve()
{
scanf("%lld%lld", &n, &m);
init();
for (int i = 1; i <= n; i++)
{
int k;
scanf("%lld", &k);
for (int j = 1; j <= k; j++)
{
int x;
scanf("%lld", &x);
p[x].push_back(i);
}
}
for (int i = 1; i <= m; i++)
{
int sz = p[i].size();
for (int j = 0; j < sz - 1; j++)
{
add(p[i][j], p[i][sz - 1]);
indegree[p[i][sz - 1]]++;
}
}
toposort();
for (int i = 1; i <= n; i++)
{
if (ans[i] != i) // 检查跟原操作顺序是否相同
{
printf("Yes\n");
for (int i = 1; i <= n; i++)
{
printf("%lld%c", ans[i], (i == n ? '\n' : ' '));
}
return;
}
}
printf("No\n");
}
signed main()
{
int T = 1;
scanf("%lld", &T);
while (T--)
{
solve();
}
return 0;
}
G - Knapsack
题意
n个物品,每个物品有w与v两种属性。现有W元(钱不必花完),能免费拿k个,问v的最大和。
思路
一眼背包,考虑到还能免费拿k个,将物品按照w升序排序,显然存在一个分界点,使得做背包的物品都在某段前缀中,而零元购的物品都在对应的后缀中。预处理后缀中取前k个v最大的,边跑背包边跟新ans。
代码
点击查看代码
#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int, int> pii;
const int mxn = 1e4 + 5;
int dp[mxn];
void solve()
{
int n, w, k;
scanf("%lld%lld%lld", &n, &w, &k);
vector<pii> v(n); // {w, v}
for (int i = 0; i < n; i++)
{
scanf("%lld%lld", &v[i].first, &v[i].second);
}
sort(v.begin(), v.end());
priority_queue<int, vector<int> , greater<int>> heap; // 小根堆维护后缀
vector<int> pre(n + 1);
int sum = 0;
for (int i = n - 1; i >= 0; i--)
{
sum += v[i].second;
heap.push(v[i].second);
if (heap.size() > k) // 取前k大的
{
sum -= heap.top();
heap.pop();
}
pre[i] = sum;
}
//01背包
int ans = -1;
for (int i = 0; i < n; i++)
{
for (int j = w; j >= v[i].first; j--)
{
dp[j] = max(dp[j], dp[j - v[i].first] + v[i].second);
}
ans = max(ans, dp[w] + pre[i + 1]); // 前i个跑背包,后边的免费拿k个
}
printf("%lld", ans);
}
signed main()
{
int T = 1;
//scanf("%lld", &T);
while (T--)
{
solve();
}
return 0;
}
I - Counter
题意
n次操作,操作分两种:归零或+1;m个条件,每个条件给定a与b,表示第a次操作后值为b。问是否存在所给情况。
思路
按a给操作排序,由于每次只能+1,故\(b \le a\);对于相邻两次操作,相差\(a_i - a_j\)次操作,可以全部加\(b_j - b_i = a_j - a_i\),也可以几次归零几次加,就可以得出范围:\(b_j \ge 0\)(全归零)且\(b_j <= s2 - a1 - 1\)(归零一次),但题目保证了\(b \ge 0\),故只用看后者。
代码
点击查看代码
#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int, int> pii;
void solve()
{
int n, m;
scanf("%lld%lld", &n, &m);
vector<pii> v(m);
for (int i = 0; i < m; i++)
{
scanf("%lld%lld", &v[i].first, &v[i].second);
}
if (m == 1) // 特判
{
printf(v[0].second > v[0].first ? "No\n" : "Yes\n");
return;
}
sort(v.begin(), v.end());
for (int i = 0; i < m - 1; i++)
{
int a1 = v[i].first, b1 = v[i].second;
int a2 = v[i + 1].first, b2 = v[i + 1].second;
if (b1 > a1 || b2 > a2)
{
printf("No\n");
return;
}
if (b2 - b1 == a2 - a1 || b2 <= a2 - a1 - 1) // 全加或归零一次后全加
{
continue;
}
else
{
printf("No\n");
return;
}
}
printf("Yes\n");
}
signed main()
{
int T = 1;
scanf("%lld", &T);
while (T--)
{
solve();
}
return 0;
}

浙公网安备 33010602011771号