NOIP2024加赛7

镜的绮想 (mirror)

签到(没签成)。

对于\(y\)相同的实点和虚点的\(x\)取中间值,最大的就是答案。

时间复杂度\(O(n^2)\)

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t; i += p)
#define drep(i,s,t,p) for(int i = s;i >= t; i -= p)
#ifdef LOCAL
    FILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
#else
    // FILE *InFile = stdin,*OutFile = stdout;
    FILE *InFile = freopen("mirror.in","r",stdin),*OutFile = freopen("mirror.out","w",stdout);
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
#define int long long
#define eb emplace_back
#define All(x) x.begin(),x.end()
const int N = 5010,V = 2e6 + 10,M = 2e6;
int n,m;
vector<int> a[V],b[V],have;
int ct[V*3];
inline void solve(){
    cin>>n>>m;
    rep(i,1,n,1){int x,y;cin>>x>>y;x += 1e6;a[x].eb(y);have.eb(x);}
    rep(i,1,m,1){int x,y;cin>>x>>y;x += 1e6;b[x].eb(y);have.eb(x);}
    sort(All(have));have.erase(unique(All(have)),have.end());
    for(auto i:have) sort(All(a[i])),sort(All(b[i]));
    int ans = 0;
    for(auto x:have){
        if(a[x].empty() || b[x].empty()) continue;
        for(auto y1:a[x]) for(auto y2:b[x])
            ct[y1+y2+M]++,ans = max(ans,ct[y1+y2+M]);
    }
    cout<<ans;
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    solve();
}

万物有灵 (animism)

半个签。(没签成)

贪心的思路是简单的,就是从第\(n\)层往上,隔一层选一层。因为第\(i\)层的点的个数 \(\ge\)\(i-1\)层点的个数。

容易发现第\(i+k\)层的点的个数\(=\)\(i\)层点的个数\(\times\prod a_i\)

用特殊性质启发正解。发现有一个\(k\)为偶数的特殊性质,想想这个怎么用。将第一层特判掉,发现将整棵树每\(k\)层分一段,每段选的是相同的,且相差为\(\prod a_i\),剩下不满\(k\)层的就额外加上即可。

发现这就是个等比数列求和,但是模数不是质数,不能直接套公式求逆元。考虑如何求这个东西,发现\(n\)很大,那么考虑d带\(\log\)的算法,目前有的做法为矩阵快速幂/倍增,这里用的是矩阵快速幂。

考虑对于所有与\(k\)同余的\(i\)的乘了几次\(q=\prod a_i\),发现总共乘了\(1+q+q^2+\cdots\)次,考虑如何用矩阵快速幂求这个东西。

具体的,设\(f_i\)表示\(q^{i-1}\),显然有\(f_{i}=f_{i-1}\times q\),考虑给定一个\(n\),如何求\(\sum\limits_{i=1}^nf_i\)

设初始的矩阵\(A=\begin{bmatrix}f_1&f_1\\\end{bmatrix}\),与其做乘法的矩阵\(B=\begin{bmatrix}1&0\\s_{k-1}&s_{k-1}\end{bmatrix}\),其中\(s_{i}=\prod\limits_{i=0}^ia_i\),然后手膜一下就ok了。

利用上面这个东西就求完了,然后对于\(k\)为奇数的暴力拓展循环节就行了。时间复杂度\(k+M\log\frac{n}{k}\),其中\(M\)为矩阵乘法的时间。

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t; i += p)
#define drep(i,s,t,p) for(int i = s;i >= t; i -= p)
#ifdef LOCAL
    FILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
#else
    // FILE *InFile = stdin,*OutFile = stdout;
    FILE *InFile = freopen("animism.in","r",stdin),*OutFile = freopen("animism.out","w",stdout);
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 1e6 + 10;
int n,k,mod,a[N],s[N];
struct Matrix{
    array<array<int,2>,2> a;
    Matrix(){memset(a.data(),0,sizeof a);}
    inline Matrix operator * (const Matrix &b) const {
        Matrix res;
        rep(k,0,1,1) rep(i,0,1,1) rep(j,0,1,1)
            res.a[i][j] = (res.a[i][j] + 1ll*a[i][k]*b.a[k][j]%mod)%mod;
        return res;
    }
}emm,base;
inline int power(int b){
    if(b < 0) return 0;
    if(b == 0) return 1;
    emm.a = {1,1,0,0};base.a={1,0,s[k-1],s[k-1]};
    for(;b;b >>= 1,base = base*base)
        if(b&1) emm = emm*base;
    return emm.a[0][0];
}
inline void solve(){
    cin>>n>>k>>mod;rep(i,0,k-1,1) cin>>a[i];
    if(k&1){rep(i,k,2*k-1,1) a[i] = a[i-k];k <<= 1;}
    s[0] = a[0];rep(i,1,k-1,1) s[i] = 1ll*s[i-1]*a[i]%mod;
    ll l = (n-1)%k+1,r = (n-1)/k,r1 = power(r),r2 = power(r-1),ans = 0;
    drep(i,l-1,0,2) ans = (ans + s[i]*r1%mod)%mod;
    rep(i,l+1,k-1,2) ans = (ans + s[i]*r2%mod)%mod;
    cout<<(ans + (n+1&1))%mod;
}
signed main(){cin.tie(nullptr)->sync_with_stdio(false);solve();}

白石溪 (creek)

\(O(n^2)\)dp是显然的,但是优化是困难的,考虑贪心。

考虑一个石头的贡献,对于一个被染成红色的石头\(i\),其贡献为\(a_i+(i-p_i)\times d\),其中\(p_i\)表示\(1\sim i\)中与\(i\)颜色相同的石头的个数,蓝色同理。那么就可以将贡献拆为\(\sum a_i+i\times d+\sum -p_i\times d\)

考虑全部为红色时,显然贡献为\(\sum\limits_{i=1}^na_i\),考虑将一个石头换为红色,那么增加的贡献为\(b_i-a_i+(i-1)\times(d-c)+(n-i)\times d\),但是如果有两个及以上的点就行不通了,考虑将这个贡献减去,加入已经选了\(i\)个点,那么需要减去的贡献为\(\frac{i\times(i-1)\times (c+d)}{2}\),简单解释一下,就是把所有已经选过的点两两组合,乘上它们两个之间互相贡献的\(c+d\)

按照\(b_i-a_i+(i-1)\times(d-c)+(n-i)\times d\)大小排序贪心选,枚举选了几个红色点即可,时间复杂度\(O(n\log n)\)

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t; i += p)
#define drep(i,s,t,p) for(int i = s;i >= t; i -= p)
#ifdef LOCAL
    FILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
    // FILE *ErrFile = freopen("err.err","w",stderr);
#else
    // FILE *InFile = stdin,*OutFile = stdout;
    FILE *InFile = freopen("creek.in","r",stdin),*OutFile = freopen("creek.out","w",stdout);
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 1e6 + 10;
ll n,c,d,a[N],b[N],_[N],ans,res,sum;
inline void solve(){
    cin>>n>>c>>d; rep(i,1,n,1)cin>>a[i]>>b[i],res += a[i];
    rep(i,1,n,1) _[i] = b[i] - a[i] + (i-1)*c+(n-i)*d;
    sort(_+1,_+1+n,greater<>());ans = res;
    rep(i,1,n,1) sum += _[i],ans = max(ans,res + sum - 1ll*i*(i-1)/2*(c+d));
    cout<<ans;
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    // solve();
}

上山岗 (uphill)

打ABC去了,不写解释。

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t; i += p)
#define drep(i,s,t,p) for(int i = s;i >= t; i -= p)
#ifdef LOCAL
    FILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
#else
    // FILE *InFile = stdin,*OutFile = stdout;
    FILE *InFile = freopen("uphill.in","r",stdin),*OutFile = freopen("uphill.out","w",stdout);
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 5e5 + 10;
int n;array<int,N> c,w,to1,to2;
struct Segment_Tree{
    struct segment_tree{
        int l,r,val;
        #define l(x) tree[x].l
        #define r(x) tree[x].r
        #define val(x) tree[x].val
    };
    array<segment_tree,N<<2> tree;
    inline void pushup(int k){val(k) = min(val(k<<1),val(k<<1|1));}
    void build(int k = 1,int l = 1,int r = n){
        l(k) = l,r(k) = r;
        if(l == r) return val(k) = c[l],void();
        int mid = (l + r) >> 1;
        build(k<<1,l,mid);build(k<<1|1,mid+1,r);
        pushup(k);
    }
    void upd(int k,int pos,int val){
        if(l(k) == r(k)) return val(k) = val,void();
        int mid = (l(k) + r(k)) >> 1;
        pos <= mid?upd(k<<1,pos,val):upd(k<<1|1,pos,val);
        pushup(k);
    }
    int qry1(int k,int x){
        if(l(k) == r(k)) return val(k) < x?l(k):n+1;
        return val(k<<1) < x?qry1(k<<1,x):qry1(k<<1|1,x);
    }
    int qry2(int k,int x){
        if(l(k) == r(k)) return val(k) < x?l(k):n+1;
        return val(k<<1|1) < x?qry2(k<<1|1,x):qry2(k<<1,x);
    }
}t1,t2;
inline void solve(){
    cin>>n;rep(i,1,n,1) cin>>c[i];rep(i,1,n,1) cin>>w[i];
    sort(w.data()+1,w.data()+1+n);t1.build(),t2.build();
    rep(i,1,n,1){
        int pos = t1.qry2(1,w[i]);
        if(pos ^ (n+1)){
            to1[pos] = i;to2[i] = pos;
            t1.upd(1,pos,0x3f3f3f3f);
        }
    }
    drep(i,n,1,1){
        int pos;
        if(!to2[i]){
            pos = t2.qry1(1,0x3f3f3f3f);
            to2[to1[pos]] = 0;
            to1[to2[i] = pos] = i;
        }
        else{
            t1.upd(1,to2[i],c[to2[i]]);
            to1[to2[i]] = 0;
			pos = t1.qry1(1,w[i]);
			to1[pos] = i,to2[i] = pos;
        }
        t1.upd(1,pos,0x3f3f3f3f);t2.upd(1,pos,0x3f3f3f3f);
    }
    rep(i,1,n,1) cout<<w[to1[i]]<<' ';
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    solve();
}
p

image

posted @ 2024-11-22 19:54  CuFeO4  阅读(34)  评论(2编辑  收藏  举报