这是今年我的最后一场,顺便写一下游记,第一场因为发挥打铁,心里很郁闷,加之最近学业加(我们学院都是硬件专业) 。那天考了四级(不能过)就和另外一个队一起坐高铁来到西安,到的时候时间还有还可以打一打热身赛,不得不说志愿者服务真好。
第一天热身赛,看了一眼,旁边都是比我们好的学校,直接签了A题,C++,JAVA都试了一下还不错,Clion好用,B题刚刚开始想对了,但不过被自己干掉了,然后试了一试机器,就走了,会宾馆点外卖。
第二天正式赛,其实挺慌的,不敢多开,就三个人看一道题,好在刚刚开场就有人过了A题,然后看A题,三个人反复确认没有读错后,大家一起写了一发,一发A,然后有人交了E,M题,就看E题和M题,M题刚刚我有点误解题意了,好在队友小G纠正过来了,然后小Z感觉E题题意不是很好,然后因为小G擅长图论,就让一起读懂了题意让他看了看,结果他想了10分钟,没有思路,然后小Z有想到了M题怎么做,我们让小G继续看看E,我和小Z写M,一会儿写出来了,测了很多数据没有问题,交WA,因为有了南京的前车之鉴,一下子看到了爆了int,该了一交A了,然后这时E题过得比H多,然后不知道怎么的大家好像多E题没有思路就都来看H题,H题一会儿也读懂了,然后大家都在想不知道怎么做,中间我口嗨了一个原根。。。然后忽然听到旁边有一个队说了一个n/2个数,肯定不会超过二的间隔不是2就是1,最多3,然后我们好像知道怎么做了,写了一会儿,WA一发,好在一下子就测出了,特殊样例,改了A了,然后还有两个小时,我们分别看了C题和E题,我想了30分钟C题确实想不到,然后我们去看E题,中间我想了个假算法,WA了,然后大家,想法层出不穷,没一个对的,然后就结束了,心想南京有这状态多好。好在最后混了一个银尾。。。。
本场比赛的唯一签到题
给一个网格,问有多少条线段两端是格点,同时中点也是格点,比赛时拿着这道题,想了一会儿,直接就上去写了,就是暴力统计,偶数长度边的数量,然后偶数偶数进行组合。
#include "bits/stdc++.h"
using namespace std;
inline int read() {
int x = 0;
bool f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
if (f) return x;
return 0 - x;
}
typedef long long ll;
const int maxn = 100000 + 10;
const ll mod = 998244353;
int n, m;
int main() {
cin >> n >> m;
ll ans = 0;
for (int i = 2; i <= n; i += 2)
ans += 1ll * (m + 1) * (n - i + 1);
for (int i = 2; i <= m; i += 2)
ans += 1ll * (n + 1) * (m - i + 1);
for (int i = 2; i <= n; i += 2) {
for (int j = 2; j <= m; j += 2) {
ans += 2ll * (n - i + 1) * (m - j + 1);
}
}
cout << ans << endl;
return 0;
}
我想说的是,题意很好的,就是求一个函数f,他自己狄利克雷卷自己k次得到的g。
这是以前学习中不曾想过的,今天偶然发现一个函数卷自己mod+1次就是自己,mod次就是 [ n = 1 ] [n=1] [n=1]
把我惊倒了,我就想和mod有关,没想到开k次方根就是一个快速幂 i n v ( k ) inv(k) inv(k)次,交了一发居然过了。我也不知道,等能证明了再来补上。
#include "bits/stdc++.h"
using namespace std;
inline int read() {
int x = 0;
bool f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
if (f) return x;
return 0 - x;
}
typedef long long ll;
const int maxn = 100000 + 10;
const ll mod = 998244353;
ll quick(ll a, ll n) {
a %= mod;
ll ans = 1;
while (n) {
if (n & 1) ans = ans * a % mod;
n >>= 1;
a = a * a % mod;
}
return ans;
}
int n, k;
ll f[maxn], g[maxn], s[maxn], ss[maxn], pro[maxn], inv_f[maxn];
void mult(ll a[], ll b[]) {
memset(s, 0, sizeof(s));
for (int i = 1; i * i <= n; i++) {
s[i * i] = (s[i * i] + a[i] * b[i] % mod) % mod;
for (int j = i + 1; i * j <= n; j++) {
s[i * j] = (s[i * j] + a[i] * b[j] % mod + a[j] * b[i] % mod) % mod;
}
}
for (int i = 1; i <= n; i++) a[i] = s[i];
}
void power(ll a[], int p) {
for (int i = 1; i <= n; i++) ss[i] = a[i];
memset(pro, 0, sizeof(pro));
pro[1] = 1;
while (p) {
if (p & 1) mult(pro, ss);
mult(ss, ss);
p >>= 1;
}
for (int i = 1; i <= n; i++) a[i] = pro[i];
}
int main() {
n = read(), k = read();
for (int i = 1; i <= n; i++) {
scanf("%lld", &f[i]);
g[i] = 1;
}
ll inv = quick(k, mod - 2);
power(f, inv);
printf("%lld", f[1]);
for (int i = 2; i <= n; i++) {
printf(" %lld", f[i]);
}
cout << endl;
return 0;
本题题意,给你一个图,这个图很特殊,源点1到汇点n的长度都相等,而且每一条源点到汇点的路径都是独立路径互补相交,类似与链,你可让一条边加1另外一条边减去1,问使得流尽量大的最少操作数。比赛的假算法就算了吧。
能过的算法是这样的,对每一条链的流进行排序,并对每一条的流进行排序,然后统计每一条链排序后,把所有链一起,流扩到下一个结点的代价。然后就可以进行贪心了,枚举每一点,剩余流量能加就加上。
#include "bits/stdc++.h"
using namespace std;
inline int read() {
int x = 0;
bool f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
if (f) return x;
return 0 - x;
}
typedef long long ll;
const int maxn = 200000 + 10;
struct edge {
int v, next, w;
} ed[maxn];
int head[maxn], cnt = 0, n, m;
void add_edge(int u, int v, int w) {
++cnt;
ed[cnt].v = v;
ed[cnt].w = w;
ed[cnt].next = head[u];
head[u] = cnt;
}
vector<int> g[maxn];
void dfs(int u, int f) {
for (int i = head[u]; i; i = ed[i].next) {
int v = ed[i].v;
if (v == f) continue;
g[cnt].push_back(ed[i].w);
dfs(v, u);
}
}
ll addflow[maxn];
int main() {
n = read(), m = read();
int u, v, w, len;
ll flow = 0;
for (int i = 1; i <= m; i++) {
u = read();
v = read();
w = read();
add_edge(u, v, w);
// add_edge(v, u, w);
flow += w;
}
cnt = 0;
for (int i = head[1]; i; i = ed[i].next) {
v = ed[i].v;
g[++cnt].push_back(ed[i].w);
dfs(v, 1);
len = g[cnt].size();
sort(g[cnt].begin(), g[cnt].end());
int l = 0;
while (l < len - 1) {
while (l < len - 1 && g[cnt][l] == g[cnt][l + 1]) l++;
if (l < len) addflow[l + 1] += g[cnt][l + 1] - g[cnt][l];
l++;
}
flow -= 1ll * g[cnt][0] * len;
}
ll ans = 0, tmp;
for (int i = 1; i < len; i++) {
if (flow < len) break;
tmp = min(addflow[i], flow / len);
ans += 1ll * tmp * i;
flow -= 1ll * tmp * len;
}
cout << ans << endl;
return 0;
}
题意类似于求,等比数列的长度,要求长度大于等于n/2向上取整。
做法,对每个相邻的数,求公比,用map进行记录,然后中间相隔一个的求公比,然后用map记录。
对出现次数大于n/8(准确多少我也不知道)的进行计数,取最大值输出就可以了。
检查的时候我用的dp要特判1的情况。现场赛,听说有很多队被卡了,我们用的map,不知道怎么的过了,下来在gym上1s能过,牛客就不行了,可能我写得不对吧。。。
#include "bits/stdc++.h"
using namespace std;
inline int read() {
int x = 0;
bool f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
if (f) return x;
return 0 - x;
}
typedef long long ll;
const int maxn = 200000 + 10;
ll mod, b[maxn];
int T, n;
ll quick(ll a, ll n, ll p) {
a %= p;
ll ans = 1;
while (n) {
if (n & 1) ans = ans * a % p;
n >>= 1;
a = a * a % p;
}
return ans;
}
int check(ll q) {
unordered_map<ll, int> dp;
int ret = 0;
if (q == 1) {
for (int i = n; i >= 1; i--) {
dp[b[i]]++;
ret = max(dp[b[i]], ret);
}
return ret;
}
for (int i = n; i >= 1; i--) {
dp[b[i]] = 1;
dp[b[i]] = max(dp[b[i] * q % mod] + 1, dp[b[i]]);
ret = max(ret, dp[b[i]]);
}
return ret;
}
unordered_map<ll, int> mp;
int main() {
T = read();
while (T--) {
mp.clear();
scanf("%d %lld", &n, &mod);
for (int i = 1; i <= n; i++) {
scanf("%lld", &b[i]);
}
ll q;
for (int i = 1; i < n; i++) {
q = b[i + 1] * quick(b[i], mod - 2, mod) % mod;
mp[q]++;
}
for (int i = 1; i < n - 1; i++) {
q = b[i + 2] * quick(b[i], mod - 2, mod) % mod;
mp[q]++;
}
// map::iterator it = mp.begin();
int ans = 0;
for (auto it=mp.begin(); it != mp.end(); it++) {
if ((it->second) >= n / 8) {
ans = max(ans, check(it->first));
}
}
if (ans < (n + 1) / 2) ans = -1;
printf("%d\n", ans);
}
return 0;
}
这道题,可能有的人被题意杀了,题意就是你可以选择一些下标的集合,价值为下标对于的a数组值,如果下标集合中满足任意, i > 1 , j > 1 , i k = j i>1,j>1,i^k=j i>1,j>1,ik=j则减去b[j]。问最大值。
做法就是,对于每一个进制进行枚举,然后记录个数 ,然后对此进行二进制状压暴力判断每一个进制的最大值就可以了
#include "bits/stdc++.h"
using namespace std;
inline int read() {
int x = 0;
bool f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
if (f) return x;
return 0 - x;
}
typedef long long ll;
const int maxn = 100000 + 10;
const ll mod = 998244353;
int n, vis[maxn], mp[maxn];
vector<int> v[maxn];
ll a[maxn], b[maxn], ans = 0;
ll solve(int st, int sz, vector<int> l) {
int ss = 0, p[20] = {0};
ll ret = 0;
for (int i = 0; i < sz; i++) {
if ((st >> i) & 1) {
p[++ss] = i + 1;
ret += a[l[i]];
}
}
for (int i = 1; i <= ss; i++) {
for (int j = i - 1; j >= 1; j--) {
if (p[i] % p[j] == 0)
ret -= b[l[p[i] - 1]];
}
}
return ret;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
for (int i = 1; i <= n; i++) {
scanf("%lld", &b[i]);
}
int cnt = 0;
for (ll i = 2; i <= n; i++) {
if (vis[i]) continue;
ll tmp = i;
mp[++cnt] = i;
while (tmp <= n) {
vis[tmp] = 1;
v[i].push_back(tmp);
tmp *= i;
}
}
ll ans = 0;
for (int i = 1; i <= cnt; i++) {
int sz = v[mp[i]].size();
if (sz == 1) {
ans += a[v[mp[i]][0]];
continue;
}
ll tmp = 0;
for (int j = 0; j < (1 << sz); j++) {
tmp = max(tmp, solve(j, sz, v[mp[i]]));
}
ans += tmp;
}
ans += a[1];
cout << ans << endl;
return 0;
}