T1 镜的绮想 (mirror)
考虑维护中点 \(y\) 坐标数量,\(mid_y=(a_y+b_y)/2\),不过不用除。枚举所有相同 \(x\) 坐标点对即可。
#include<bits/stdc++.h>
using namespace std;
constexpr int N = 5e3 + 5, MX = 2e6 + 5;
int a[N<<1], pos[N<<1], cnt, ans, val[MX<<1];
struct node{ int x, y; }tn[N], fn[N];
vector<int> tvec[N<<1], fvec[N<<1];
int main(){
freopen("mirror.in", "r", stdin); freopen("mirror.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m; cin>>n>>m;
for(int i=1; i<=n; ++i) cin>>tn[i].x>>tn[i].y, a[++cnt] = tn[i].x;
for(int i=1; i<=m; ++i) cin>>fn[i].x>>fn[i].y, a[++cnt] = fn[i].x;
sort(a+1, a+1+cnt); cnt = unique(a+1, a+1+cnt) - a - 1;
for(int i=1; i<=n; ++i) tvec[lower_bound(a+1, a+1+cnt, tn[i].x)-a].emplace_back(tn[i].y);
for(int i=1; i<=m; ++i) fvec[lower_bound(a+1, a+1+cnt, fn[i].x)-a].emplace_back(fn[i].y);
for(int i=1; i<=cnt; ++i) if(!tvec[i].empty() && !fvec[i].empty())
for(int tv : tvec[i]) for(int fv : fvec[i])
++val[tv + fv + MX], ans = max(ans, val[tv + fv + MX]);
return cout<<ans, 0;
}
T2 万物有灵 (animism)
不知道最大独立集是什么,然后就跳了,我有可能真的错过了 100pts
规律是简单的,我们称深度为 \(1\sim k\) 的树为基础树。那么最大独立集即为从树的最底层开始,取一层跳过一层以此类推。考虑利用一下周期性,当 \(k\) 为偶数的时候计算出基础树的奇数层的节点总数,和偶数层的节点总数,然后计算一下整棵树包含多少颗基础树。令 \(b_k\) 表示基础树的叶子数量,那么基础树的数量即为 \(1+b_k+b_k^2+b_k^3\dots\),等比数列求和即可。不过考虑到没有逆元的情况,可以使用矩阵乘法或者递归求解。对于 \(k\) 为奇数的情况,直接将 \(k*2\) 即可转化。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define it __int128
constexpr int N = 5e5 + 5;
int n, k, M, a[N<<1], b[N<<1];
inline int qpow(int a, int k){
int res = 1; while(k){
if(k & 1) res = (it)res * a % M;
a = (it)a * a % M; k >>= 1;
} return res;
}
inline int add(initializer_list<int> Add){
int res = 0;
for(int v : Add) res = res + v >= M ? res + v - M : res + v;
return res;
}
inline int mul(initializer_list<int> Mul){
int res = 1;
for(int v : Mul) res = (it)res * v % M;
return res;
}
inline void eg(int a, int b, int &x, int &y){
if(!b) return x = 1, y = 0, void();
eg(b, a%b, y, x); y -= a / b * x;
}
inline int inv(int k){
int x = 0, y = 0; eg(k, M, x, y);
x = ((x % M) + M) % M;
if(x < 0) printf("fvv\n"), exit(0);
return x;
}
signed main(){
freopen("animism.in", "r", stdin); freopen("animism.out", "w", stdout);
cin>>n>>k>>M;
for(int i=1; i<=k; ++i) cin>>a[i];
for(int i=k+1; i<=k<<1; ++i) a[i] = a[i-k];
if(k & 1) k *= 2;
b[1] = a[1]; int sum1 = a[1], sum2 = 0;
for(int i=2; i<=k; ++i){
b[i] = mul({b[i-1], a[i]});
if(i & 1) sum1 = add({sum1, b[i]});
else sum2 = add({sum2, b[i]});
}
if(!((n % k) & 1)){
int res = 1, lst = qpow(b[k], n/k);
int sum = mul({lst-1, inv(b[k]-1)});
res = add({res, mul({sum, sum2})});
for(int i=2; i<=n%k; i+=2)
res = add({res, mul({b[i], lst})});
cout<<abs(res); exit(0);
} else {
int res = 0, lst = qpow(b[k], n/k);
int sum = mul({lst-1, inv(b[k]-1)});
res = add({res, mul({sum, sum1})});
for(int i=1; i<=n%k; i+=2)
res = add({res, mul({b[i], lst})});
cout<<abs(res); exit(0);
}
}
T3 白石溪 (creek)
考场上苦思冥想如何优化 45pts dp 没想到是个贪心……
没太明白,颓过去的……
#include<bits/stdc++.h>
using namespace std;
constexpr int B = 1 << 15;
char buf[B], *p1 = buf, *p2 = buf;
#define gt() (p1==p2 && (p2=(p1=buf)+fread(buf, 1, B, stdin), p1==p2) ? EOF : *p1++)
template <typename T> inline void rd(T &x){
x = 0; int f = 0; char ch = gt();
for(; !isdigit(ch); ch = gt()) f ^= ch == '-';
for(; isdigit(ch); ch = gt()) x = (x<<1) + (x<<3) + (ch^48);
x = f ? -x : x;
}
template <typename T, typename ...TT> inline void rd(T &x, TT &...y){ rd(x), rd(y...); }
#define int long long
int p[1000005];
signed main(){
freopen("creek.in", "r", stdin); freopen("creek.out", "w", stdout);
int n, c, d; rd(n, c, d); int ans = 0, res = 0;
for(int i=1, a, b; i<=n; ++i){
rd(a, b); ans += a;
p[i] = a - b - (i-1) * c - (n-i) * d;
} sort(p+1, p+1+n); res = ans;
for(int i=1; i<=n; ++i) ans -= p[i], res = max(res, ans - (c+d)*i*(i-1)/2);
return printf("%lld", res), 0;
}
T4 上山岗 (uphill)
首先考虑构造出匹配数最多的方案,采用 田忌赛马 策略,先将能力值排序,从小到大枚举每一个登山队员找到最靠右的合法匹配位置,这样构造下来字典序是较小的,并且匹配数是最多的。
然后考虑在保证匹配数最多的情况下移动。贪心地,按能力值从大到小枚举队员。如果该队员当前有匹配的位置,那么找到最左端的未匹配的位置来置换,这样的位置是确定下来的;如果没有匹配的位置,那就随便找到一个最靠左没有被确定的位置,如果这个位置被其他人匹配过了,那就强制把那个人拆下来,把现在这个人换上去。因为比他小的人都有匹配,那这个人一定也有匹配。由于从大到小确定位置构造,这样的字典序一定最大。
具体维护方式可以使用两颗线段树,一棵维护没有被匹配的位置的最小值,一棵维护没有被确定的位置的最小值。支持单点修改,查最靠左、右最小值,线段树上二分即可。复杂度 \(\mathcal{O}(n\log n)\)。
#include<bits/stdc++.h>
using namespace std;
constexpr int N = 5e5 + 5, Inf = 1e9;
int n, a[N], b[N], mch[N], ans[N];
struct ST{
#define ls (id << 1)
#define rs (id << 1 | 1)
#define pushup(id) t[id].mn = min(t[ls].mn, t[rs].mn)
struct node{ int l, r, mn; }t[N<<2];
inline void build(int id, int l, int r){
t[id] = node{l, r, 0};
if(l == r) return t[id].mn = a[l], void();
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid+1, r);
pushup(id);
}
inline void modify(int id, int pos, int val){
if(pos <= 0 || pos > n) return;
if(t[id].l == t[id].r) return t[id].mn = val, void();
int mid = (t[id].l + t[id].r) >> 1;
if(pos <= mid) modify(ls, pos, val);
else modify(rs, pos, val);
pushup(id);
}
inline int query_l(int id, int val){
if(t[id].mn >= val) return 0;
if(t[id].l == t[id].r) return t[id].l;
if(t[ls].mn < val) return query_l(ls, val);
else return query_l(rs, val);
}
inline int query_r(int id, int val){
if(t[id].mn >= val) return 0;
if(t[id].l == t[id].r) return t[id].l;
if(t[rs].mn < val) return query_r(rs, val);
else return query_r(ls, val);
}
} ST1, ST2;
int main(){
freopen("uphill.in", "r", stdin); freopen("uphill.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n;
for(int i=1; i<=n; ++i) cin>>a[i];
for(int i=1; i<=n; ++i) cin>>b[i];
sort(b+1, b+1+n); ST1.build(1, 1, n); ST2.build(1, 1, n);
for(int i=1, pos; i<=n; ++i){
pos = ST1.query_r(1, b[i]);
ans[mch[i] = pos] = i;
ST1.modify(1, pos, Inf);
}
for(int i=n, pos; i>=1; --i){
if(mch[i]){
ST1.modify(1, mch[i], a[mch[i]]);
pos = ST1.query_l(1, b[i]);
ans[mch[i]] = 0; ans[pos] = i;
} else {
pos = ST2.query_l(1, Inf);
if(ans[pos]) mch[ans[pos]] = 0;
ans[pos] = i;
}
ST1.modify(1, pos, Inf), ST2.modify(1, pos, Inf);
}
for(int i=1; i<=n; ++i) cout<<b[ans[i]]<<' ';
return 0;
}
标签:2024.11,return,22,noip,int,res,pos,val,id
From: https://www.cnblogs.com/xiaolemc/p/18563552