郑州集训题目合集

Day1

NOIP 模拟赛 1

A

原来的假了。
感谢 @Kenma 提供 Hack ,我又补了一组
input:

2
4
aaaa
abcb
bbbb
4
aaaa
aaaa
bbbb

ans:

0
1

原题数据极水,希望不要再被 Hack。
性质:答案只能是 \(0,1,3\)
\(i+1=j\),则答案不可能超过 \(3\)
尝试贪心地构造更小的答案。注意到 \(a_1\) 是必选的。首先扫一遍 \(b\),找到不等于 \(a_1\) 的第一个位置(若没有就令 \(i=1\)),接着扫 \(c\),若 \(c_{j+1}\ne a_1\land c_{j+1}\ne b_{i+1}\),就更新一次答案。处理出所有字符第一次出现在 \(b\) 中的位置 \(pre\)。则若字符不等于 \(a_1\),则令其等于 \(i+1\)。然后找 \(c_{j+1}\)。总体时间复杂度 \(O(n|\sum|)\)

code
#include<iostream>
using namespace std;
typedef unsigned long long ull;
constexpr int N=1e5+10;
int t,n,ans,pre[N];
char a[N],b[N],c[N];
int main(){
    freopen("lcp.in","r",stdin);
    freopen("lcp.out","w",stdout);
    scanf("%d",&t);
    while(t--){
        scanf("%d %s %s %s",&n,a+1,b+1,c+1);
        int i=1,j=0;
        ans=3;
        for(int k=1;k<=n;k++)
            if(!pre[b[k]-'a'])
                pre[b[k]-'a']=k;
        for(int k=2;k<=n;k++)
            if(b[k]!=a[1]){
                i=k;
                break;
            }
        for(int k=i+2;k<=n;k++)
            if(c[k]!=b[i+1]&&c[k]!=a[i+1]){
                ans=min(ans,(a[1]==b[i+1])+(b[i+1]==c[j+1])+(c[j+1]==a[1]));
                break;
            }
        for(int ch=0;ch<26;ch++){
            if(ch+'a'==a[1]||!pre[ch]) continue;
            i=pre[ch]-1,j=i+1;
            for(int k=i+2;k<=n;k++)
                if(c[k]!=b[i+1]){
                    j=k-1;
                    if(c[k]!=a[1]) break;
                }
            ans=min(ans,(a[1]==b[i+1])+(b[i+1]==c[j+1])+(c[j+1]==a[1]));
        }
        printf("%d\n",ans);
    }
    return 0;
}

B

容易发现,以 \(a_i\) 为第一关键字升序,\(b_i\) 为第二关键字降序排序可得答案。这样算出的答案是最优的。
朴素计算的复杂度是 \(O(n^2)\),尝试优化。对于 \(j<i\) 的部分,显然 \(a_j\le a_i\) 恒成立。对于 \(j>i\),分为 \(a_i=a_j\)\(a_i<a_j\) 两种。可以预处理 \(nxt\) 数组表示当前相同数连续段末尾位置辅助计算。总体时间复杂度 \(O(n\log n)\)。瓶颈在于排序。

code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
template<typename T>
inline void read(T &x){
    bool f=0;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    if(f) x=~x+1;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){read(x);read(args...);}
typedef long long ll;
constexpr int N=1e6+10;
struct node{
    int a,b;
    friend bool operator<(const node &x,const node &y){
        return x.a==y.a?x.b>y.b:x.a<y.a;
    }
}p[N];
int n,nxt[N];
ll sum[N],sum2[N];
int main(){
    freopen("perm.in","r",stdin);
    freopen("perm.out","w",stdout);
    read(n);
    for(int i=1;i<=n;i++)
        read(p[i].a,p[i].b);
    sort(p+1,p+1+n);
    for(int i=n;i;i--)
        if(p[i].a==p[i+1].a) nxt[i]=nxt[i+1];
        else nxt[i]=i;
    ll res=0;
    for(int i=1;i<=n;i++){
        sum[i]=sum[i-1]+p[i].b;
        sum2[i]=sum2[i-1]+(ll)p[i].b*p[i].b;
    }
    for(int i=1;i<=n;i++){
        res+=(ll)(i-1)*p[i].b-sum[i-1];
        int j=nxt[i];
        if(i!=j)
            res+=(ll)(j-i)*p[i].b*p[i].b-2ll*p[i].b*(sum[j]-sum[i])+sum2[j]-sum2[i];
        res+=(ll)(n-j)*p[i].b-(sum[n]-sum[j]);
    }
    printf("%lld\n",res);
    return 0;
}

C

史。

D

这个拆贡献很好啊,来不及写了就先记一下思路。
可以将 \(x,y\) 双方贡献拆成一方的贡献。

  • \(x\) 身份是 \(0\),贡献为 \(a_x^2+a_x\cdot a_y\)

  • \(x\) 身份是 \(1\),贡献为 \(a_x^2-a_x\cdot a_y\)

只考虑互相指认的情况。显然有解当且仅当互相指认关系构成二分图。黑白染色即可求出这一部分的贡献。
对于连出的若干连通分量,做一个背包计算答案。

Day2

线段树

算术天才⑨与等差数列

绝世勾巴题。判断一个区间能重排成公差为 \(k\) 的等差数列的条件是:

  • 区间的最小值作为首项,最大值作为末项构造出的公差 \(d\) 等于 \(k\)

  • 区间没有重复的值;

  • 区间所有相邻两项之差的绝对值的 \(\gcd\) 等于 \(k\)

保证区间无相同数可以维护每个数的前驱,保证区间前驱最大值小于区间左端点即可。可以用 std::mapstd::set 辅助维护。
原题数据极水,通过后可以尝试 Hack,注意:Hack 数据有一点小问题,询问区间可能会有 \(l>r\) 的情况。遇到这种情况请交换 \(l,r\)

code
#include<cstdio>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
template<typename T>
inline void read(T &x){
    bool f=0;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    if(f) x=~x+1;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){read(x);read(args...);}
typedef long long ll;
constexpr int N=3e5+10;
map<int,set<int>> mp;
struct node{
    int maxn,minn,maxpre;
    inline friend node operator+(const node &x,const node &y){
        if(x.maxn==-1) return y;
        if(y.maxn==-1) return x;
        node res;
        res.maxn=max(x.maxn,y.maxn);
        res.minn=min(x.minn,y.minn);
        res.maxpre=max(x.maxpre,y.maxpre);
        return res;
    }
}tree1[N<<2];
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
int tree2[N<<2],n,m,P;
inline void modify(int x,int k){
    int old=tree1[x+P].maxn;
    auto it=mp[old].upper_bound(x),it1=it;
    if(it!=mp[old].end()){
        int i=*it+P;
        int pre=--it==mp[old].begin()?0:*--it;
        tree1[i].maxpre=pre;
        for(i>>=1;i;i>>=1)
            tree1[i].maxpre=max(tree1[ls(i)].maxpre,tree1[rs(i)].maxpre);
    }
    mp[old].erase(--it1);
    it=mp[k].upper_bound(x);
    if(it!=mp[k].end()){
        int i=*it+P;
        tree1[i].maxpre=x;
        for(i>>=1;i;i>>=1)
            tree1[i].maxpre=max(tree1[ls(i)].maxpre,tree1[rs(i)].maxpre);
    }
    it=mp[k].insert(x).first;
    old=tree1[x+P].maxn;
    tree1[x+P]={k,k,it==mp[k].begin()?0:*--it};
    for(int i=(x+P)>>1;i;i>>=1)
        tree1[i]=tree1[ls(i)]+tree1[rs(i)];
    if(x<n){
        tree2[x+P+1]+=old;
        tree2[x+P+1]-=k;
        for(int i=(x+P+1)>>1;i;i>>=1)
            tree2[i]=__gcd(abs(tree2[ls(i)]),abs(tree2[rs(i)]));
    }
    tree2[x+P]-=old;
    tree2[x+P]+=k;
    for(int i=(x+P)>>1;i;i>>=1)
        tree2[i]=__gcd(abs(tree2[ls(i)]),abs(tree2[rs(i)]));
}
inline node query1(int l,int r){
    l+=P-1,r+=P+1;
    node resl={-1},resr={-1};
    while(l^1^r){
        if(~l&1) resl=resl+tree1[l^1];
        if(r&1) resr=tree1[r^1]+resr;
        l>>=1,r>>=1;
    }
    return resl+resr;
}
inline int query2(int l,int r){
    l+=P-1,r+=P+1;
    int res=0;
    while(l^1^r){
        if(~l&1) res=__gcd(res,abs(tree2[l^1]));
        if(r&1) res=__gcd(res,abs(tree2[r^1]));
        l>>=1,r>>=1;
    }
    return res;
}
int main(){
    read(n,m);
    P=1;
    while(P<=n+1) P<<=1;
    for(int i=1,a,last;i<=n;i++){
        read(a);
        tree1[P+i]={a,a};
        if(mp[a].empty()) tree1[P+i].maxpre=0;
        else tree1[P+i].maxpre=*--mp[a].end();
        mp[a].insert(i);
        if(i>1) tree2[P+i]=a-last;
        last=a;
    }
    for(int i=P-1;i;i--){
        tree1[i]=tree1[ls(i)]+tree1[rs(i)];
        tree2[i]=__gcd(abs(tree2[ls(i)]),abs(tree2[rs(i)]));
    }
    int op,x,y,k,cnt=0;
    while(m--){
        read(op);
        if(op==1){
            read(x,k);
            x^=cnt,k^=cnt;
            modify(x,k);
        }
        else{
            read(x,y,k);
            x^=cnt,y^=cnt,k^=cnt;
            if(x>y) swap(x,y);
            if(x==y){
                printf("Yes\n"),++cnt;
                continue;
            }
            node u=query1(x,y);
            if(k==0){
                if(u.maxn==u.minn) printf("Yes\n"),++cnt;
                else printf("No\n");
                continue;
            }
            int d=query2(x+1,y);
            if(u.maxn-u.minn==(ll)(y-x)*k&&d==k&&u.maxpre<x)
                printf("Yes\n"),++cnt;
            else printf("No\n");
        }
    }
    return 0;
}

【模板】三维偏序(陌上花开)

自学了一手 cdq 分治。分治过程中的排序可以直接利用分治结构进行归并。

code
#include<cstdio>
#include<algorithm>
using namespace std;
template<typename T>
inline void read(T &x){
    bool f=0;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    if(f) x=~x+1;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){read(x);read(args...);}
constexpr int N=2e5+10;
struct node{
    int a,b,c,val,ans;
    bool operator==(const node &x)const{
        return a==x.a&&b==x.b&&c==x.c;
    }
}p[N],temp[N];
bool cmpa(const node &x,const node &y){
    return x.a==y.a?(x.b==y.b?x.c<y.c:x.b<y.b):x.a<y.a;
}
bool cmpb(const node &x,const node &y){
    return x.b==y.b?x.c<y.c:x.b<y.b;
}
int n,k,tot,ans[N],tree[N];
#define lowbit(x) (x&-x)
void modify(int x,int c){
    for(;x<=k;x+=lowbit(x)) tree[x]+=c;
}
int query(int x){
    int res=0;
    for(;x;x^=lowbit(x)) res+=tree[x];
    return res;
}
void cdq(int l,int r){
    if(l==r) return;
    int mid=(l+r)>>1;
    cdq(l,mid),cdq(mid+1,r);
    int x=l,y=mid+1;
    while(y<=r){
        while(x<=mid&&p[x].b<=p[y].b){
            modify(p[x].c,p[x].val);
            ++x;
        }
        p[y].ans+=query(p[y].c);
        ++y;
    }
    for(int i=l;i<x;i++) modify(p[i].c,-p[i].val);
    x=l,y=mid+1;
    int cnt=l-1;
    while(x<=mid&&y<=r){
        if(cmpb(p[x],p[y])) temp[++cnt]=p[x++];
        else temp[++cnt]=p[y++];
    }
    while(x<=mid) temp[++cnt]=p[x++];
    while(y<=r) temp[++cnt]=p[y++];
    for(int i=l;i<=r;i++) p[i]=temp[i];
}
int main(){
    read(n,k);
    for(int i=1;i<=n;i++)
        read(p[i].a,p[i].b,p[i].c),p[i].val=1;
    sort(p+1,p+1+n,cmpa);
    for(int i=1;i<=n;i++){
        if(p[i]==p[tot]) ++p[tot].val;
        else p[++tot]=p[i];
    }
    cdq(1,tot);
    for(int i=1;i<=tot;i++)
        ans[p[i].ans+p[i].val-1]+=p[i].val;
    for(int i=0;i<n;i++)
        printf("%d\n",ans[i]);
    return 0;
}

Day3

NOIP 模拟赛 2

A

容易发现,同一个连通块的答案相同,记 \(sum\) 为所有 \(1\) 的数量,\(size\) 为当前连通块大小,该连通块的答案就是 \(size/(sum+1)\)。枚举所有 \(0\),模拟加入点后合并连通块,注意去重。时间复杂度 \(O(nm)\)

code
#include<iostream>
using namespace std;
constexpr int N=2010;
int n,m,s[N][N],fa[N*N],siz[N*N],sum;
bool vis[N][N];
void read(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%1d",&s[i][j]);
}
inline int id(int x,int y){return (x-1)*m+y;}
void debug(){
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
//            printf("%d ",s[i][j]);
            printf("%d ",siz[fa[id(i,j)]]);
        }
        printf("\n");
    }
}
constexpr int fx[]={1,0,-1,0},fy[]={0,1,0,-1};
struct node{int x,y;}q[N*N];
int l,r;
void bfs(int sx,int sy){
    l=1,r=0;
    q[++r]={sx,sy};
    int root=id(sx,sy);
    fa[root]=root;
    siz[root]=1;
    vis[sx][sy]=1;
    while(l<=r){
        node u=q[l++];
        for(int i=0;i<4;i++){
            int x=u.x+fx[i],y=u.y+fy[i];
            if(x<1||y<1||x>n||y>m||!s[x][y]||vis[x][y])
                continue;
            vis[x][y]=1;
            q[++r]={x,y};
            ++siz[root];
            fa[id(x,y)]=root;
        }
    }
}
void get_size(){//获取不加入时的连通块大小
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(s[i][j]&&!vis[i][j])
                bfs(i,j);
            sum+=s[i][j];
        }
}
int temp[5],cnt;
inline bool find(int x){
    for(int i=1;i<=cnt;i++)
        if(temp[i]==x) return 1;
    return 0;
}
void solve(){
    get_size();
    double ans=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(s[i][j]) continue;
            int sumk=1;
            cnt=0;
            for(int k=0;k<4;k++){
                int x=i+fx[k],y=j+fy[k];
                if(x<1||y<1||x>n||y>m)
                    continue;
                int belong=fa[id(x,y)];
                if(!s[x][y]||find(belong))
                    continue;
                sumk+=siz[belong];
                temp[++cnt]=belong;
            }
            ans=min(ans,(double)sumk/(sum+1));
        }
    printf("%.9lf\n",ans);
}
void solve_sub5(){//特殊性质 A,显然可以自己形成一个大小为 1 的连通块
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            sum+=s[i][j];
    printf("%.9lf\n",1.0/(sum+1));
}
int main(){
    freopen("plague.in","r",stdin);
    freopen("plague.out","w",stdout);
    read();
    solve();
    return 0;
}

B

我以为写了个 \(O(nq\log n)\),但好像是调和级数就变成 \(O(q\log^2n)\) 了,不太清楚。一个重要转化是从中间往两边跳变为从两边往中间跳。

code
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
template<typename T>
inline void read(T &x){
    bool f=0;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    if(f) x=~x+1;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){read(x);read(args...);}
constexpr int N=1e6+10;
int n,q,x[N],maxn;
vector<int> from[N];
struct query{
    int val,id;
    bool operator<(const query &x)const{
        return val<x.val;
    }
}k[N];
void solvesub1(){
    for(int c=1;c<=q;c++){
        read(k[c].val);
        if(k[c].val<maxn){
            printf("-1 ");
            continue;
        }
        int ans=2e9;
        for(int mid=1;mid<=n;mid++){
            int l=mid,r=mid,cnt=0;
            while(l>1||r<n){
                if(l>1) l=lower_bound(x+1,x+1+n,x[l]-k[c].val)-x;
                if(r<n) r=upper_bound(x+1,x+1+n,x[r]+k[c].val)-x-1;
                ++cnt;
            }
            if(ans>cnt) ans=cnt,from[c].clear(),from[c].push_back(mid);
            else if(ans==cnt) from[c].push_back(mid);
        }
        printf("%d ",ans);
    }
    printf("\n");
//    for(int i=1;i<=q;i++){
//        printf("%d:",k[i]);
//        for(int x:from[i])
//            printf("%d ",x);
//        printf("\n");
//    }
}
int ans[N],cnt;
void solve2(){
    for(int i=1;i<=q;i++) read(k[i].val),k[i].id=i;
    sort(k+1,k+1+q);
    int d=maxn;
    for(int l=1,r=n;l<r;){
        l=upper_bound(x+1,x+1+n,x[l]+d)-x-1;
        r=lower_bound(x+1,x+1+n,x[r]-d)-x;
        ++cnt;
    }
    for(int i=1;i<=q;i++){
        if(k[i].val<maxn){
            ans[k[i].id]=-1;
            continue;
        }
        if(k[i].val<=d){
            ans[k[i].id]=cnt;
            continue;
        }
        d=k[i].val;
        cnt=0;
        for(int l=1,r=n;l<r;){
            l=upper_bound(x+1,x+1+n,x[l]+d)-x-1;
            r=lower_bound(x+1,x+1+n,x[r]-d)-x;
            ++cnt;
        }
        ans[k[i].id]=cnt;
    }
    for(int i=1;i<=q;i++)
        printf("%d ",ans[i]);
}
int main(){
    freopen("beacon.in","r",stdin);
    freopen("beacon.out","w",stdout);
    read(n,q);
    for(int i=1;i<=n;i++) read(x[i]);
    for(int i=2;i<=n;i++) maxn=max(maxn,x[i]-x[i-1]);
    solve2();
    return 0;
}

C

史。

D

史。

Day4

树上问题

[SDOI2016] 游戏

发现修改操作类似插入线段,考虑使用树链剖分+李超线段树维护,设 \(dis_u\) 表示 \(u\) 到根节点的距离,这里显然满足一条重链上 \(dis\) 单调递增,令 \(x=dis_x\) 即可用李超线段树维护,设 \(s,t\) 的 lca 为 \(x\),将一条路径拆成 \(s\to x\)\(x\to t\) 两部分。
\(p\) 为路径上的一个点。

  • 对于 \(s\to x\),这一段为 \(a\times (dis_p-dis_s)+b\),则插入线段 \(y=-ax+a\times dis_s+b\)

  • 对于 \(x\to t\),这一段为 \(a\times(dis_s+dis_p-2\times dis_x)+b\),则插入线段 \(y=ax+a\times(dis_s-2\times dis_x)+b\)

查询最小值比较平凡。

code
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
template<typename T>
inline void read(T &x){
    bool f=0;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    if(f) x=~x+1;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){read(x);read(args...);}
typedef long long ll;
constexpr int N=1e5+10;
constexpr ll inf=123456789123456789ll;
struct edge{int v,w;};
vector<edge> e[N];
int n,m;
int siz[N],top[N],dfncnt,dfn[N],son[N],fa[N],dep[N],idfn[N];
ll dis[N];
void dfs1(int u,int f){
    fa[u]=f;
    siz[u]=1;
    dep[u]=dep[f]+1;
    for(auto to:e[u]){
        if(to.v==f) continue;
        dis[to.v]=dis[u]+to.w;
        dfs1(to.v,u);
        siz[u]+=siz[to.v];
        if(siz[to.v]>siz[son[u]])
            son[u]=to.v;
    }
}
void dfs2(int u,int topf){
    dfn[u]=++dfncnt;
    idfn[dfncnt]=u;
    top[u]=topf;
    if(son[u]) dfs2(son[u],topf);
    for(auto to:e[u]){
        if(to.v==fa[u]||to.v==son[u]) continue;
        dfs2(to.v,to.v);
    }
}
struct line{
    ll k,b;
    inline ll Y(int x){return dis[idfn[x]]*k+b;}
};
#define ls (u<<1)
#define rs (u<<1|1)
struct node{
    line L;
    ll minn;
    node():L({0,inf}),minn(inf){}
}tree[N<<2];
void push_up(int u){
    tree[u].minn=min({tree[u].minn,tree[ls].minn,tree[rs].minn});
}
void modify(int u,int l,int r,int x,int y,line k){
    if(x<=l&&y>=r){
        ll ly1=tree[u].L.Y(l),ry1=tree[u].L.Y(r);
        ll ly2=k.Y(l),ry2=k.Y(r);
        if(ly2<ly1&&ry2<ry1){
            tree[u].L=k;
            tree[u].minn=min({tree[u].minn,ly2,ry2});
        }
        else if(ly2<ly1||ry2<ry1){
            int mid=(l+r)>>1;
            if(k.Y(mid)<tree[u].L.Y(mid)) swap(k,tree[u].L);
            if(k.Y(l)<tree[u].L.Y(l))
                modify(ls,l,mid,x,y,k);
            else modify(rs,mid+1,r,x,y,k);
            tree[u].minn=min({tree[u].minn,tree[u].L.Y(l),tree[u].L.Y(r)});
            push_up(u);
        }
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid) modify(ls,l,mid,x,y,k);
    if(y>mid) modify(rs,mid+1,r,x,y,k);
    push_up(u);
}
ll query(int u,int l,int r,int x,int y){
    if(x<=l&&y>=r) return tree[u].minn;
    int ql=max(l,x),qr=min(r,y);
    ll res=min(tree[u].L.Y(ql),tree[u].L.Y(qr));
    int mid=(l+r)>>1;
    if(x<=mid) res=min(res,query(ls,l,mid,x,y));
    if(y>mid) res=min(res,query(rs,mid+1,r,x,y));
    return res;
}
void change(int x,int y,line k){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        modify(1,1,n,dfn[top[x]],dfn[x],k);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    modify(1,1,n,dfn[x],dfn[y],k);
}
ll ask(int x,int y){
    ll res=inf;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        res=min(res,query(1,1,n,dfn[top[x]],dfn[x]));
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    res=min(res,query(1,1,n,dfn[x],dfn[y]));
    return res;
}
int lca(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        x=fa[top[x]];
    }
    return dep[x]<dep[y]?x:y;
}
int main(){
    read(n,m);
    for(int i=1,u,v,w;i<n;i++){
        read(u,v,w);
        e[u].push_back({v,w});
        e[v].push_back({u,w});
    }
    dfs1(1,0);
    dfs2(1,1);
    int op,s,t,a,b;
    while(m--){
        read(op,s,t);
        if(op==1){
            read(a,b);
            int x=lca(s,t);
            change(s,x,{-a,a*dis[s]+b});
            change(x,t,{a,a*(dis[s]-2*dis[x])+b});
        }
        else printf("%lld\n",ask(s,t));
    }
    return 0;
}

Day5

NOIP 模拟赛 3

C

树上莫队。

Day6

NOIP 模拟赛 4

A

注意到最多操作 \(40\) 次,直接暴力上个二分优化即可。

code
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
template<typename T>
inline void read(T &x){
    bool f=0;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    if(f) x=~x+1;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){read(x);read(args...);}
constexpr int N=5e5+10;
int n,r,tot,ans[N],a[N],q[N],ql,qr,cnt;
inline bool check(int mid){
    return mid-r<=q[ql]&&mid+r>=q[qr];
}
int main(){
    freopen("mine.in","r",stdin);
    freopen("mine.out","w",stdout);
    read(n,r);
    for(int i=1;i<=n;i++) read(a[i]);
    sort(a+1,a+1+n);
    while(cnt<n){//40
        ql=1,qr=0;
        int maxsiz=0,resl=1,resr=1;
        ++tot;
        for(int i=1;i<=n-cnt;i++){
            q[++qr]=a[i];
            while(q[ql]+2*r<q[qr]) ++ql;
            if(qr-ql+1>maxsiz){
                maxsiz=qr-ql+1;
                int pl=a[ql]-r,pr=a[qr]+r;
                while(pl<=pr){
                    int mid=(pl+pr)>>1;
                    if(check(mid)) pr=mid-1;
                    else pl=mid+1;
                }
                ans[tot]=pl;
                resl=ql,resr=qr;
            }
        }
        for(int i=1,j=0;i<=n-cnt;i++){
            if(i>=resl&&i<=resr) continue;
            a[++j]=a[i];
        }
        cnt+=maxsiz;
    }
    printf("%d\n",tot);
    for(int i=1;i<=tot;i++)
        printf("%d ",ans[i]);
    return 0;
}

B

bitset 魔怔题。

C

不会。

D

史。

Day7

出去玩了。

Day8

没写题。

Day9

数论

【UR #1】缩进优化

额被骗了,根本就不是整除分块啊。
题意:给出序列 \(a_i\),求 \(x\) 使得最小化

\[\sum_{i=1}^n\left\lfloor \frac{a_i}{x}\right\rfloor+(a_i\bmod x) \]

首先推式子,原式变为

\[\sum_{i=1}^na_i-\sum_{i=1}^n\left\lfloor \frac{a_i}{x}\right\rfloor\cdot (x-1) \]

则只需最大化第二个 \(\sum\) 后那一部分。
直接枚举 \(x\) 并求答案,由于 \(x\) 在分母上,所以复杂度是调和级数 \(O(n\log n)\),开一个桶并维护前缀和即可。

code
#include<cstdio>
#include<algorithm>
using namespace std;
template<typename T>
inline void read(T &x){
    bool f=0;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    if(f) x=~x+1;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){read(x);read(args...);}
typedef long long ll;
constexpr int N=1e6+10;
int n,a[N],b[N],sum[N];
ll ans,res,suma;
int main(){
    read(n);
    for(int i=1;i<=n;i++) read(a[i]),++b[a[i]],suma+=a[i];
    sum[0]+=b[0];
    for(int i=1;i<=1e6;i++) sum[i]=sum[i-1]+b[i];
    ans=1e18;
    for(int i=1;i<=1e6;i++){
        res=0;
        int l=i,r=2*i-1;
        ll cnt=1;
        while(1){
            if(r>1e6){
                r=1e6;
                res+=cnt*(sum[r]-sum[l-1]);
                break;
            }
            res+=cnt*(sum[r]-sum[l-1]);
            ++cnt;
            l+=i,r+=i;
        }
        ans=min(ans,suma-res*(i-1));
    }
    printf("%lld\n",ans);
    return 0;
}

Day10

NOIP 模拟赛 6

A

唐题。

B

唐题。

C

容易想到二分,但是觉得太屎没做分讨。有类似题目

D

根号分治解决独立集问题。
无向图三元环数量最多 \(O(m\sqrt m)\) 个。

Day11

贪心

Difficult Mountain

结论:按照 \(\max(s_i,a_i)\) 第一关键字,\(s_i\) 第二关键字升序排序,能选则选。证明。

code
#include<cstdio>
#include<algorithm>
using namespace std;
template<typename T>
inline void read(T &x){
    bool f=0;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    if(f) x=~x+1;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){read(x);read(args...);}
constexpr int N=5e5+10;
int n,d,ans;
struct node{
    int s,a;
    bool operator<(const node &x)const{
        if(max(s,a)==max(x.s,x.a))
            return s<x.s;
        return max(s,a)<max(x.s,x.a);
    }
}p[N];
int main(){
    read(n,d);
    for(int i=1;i<=n;i++)
        read(p[i].s,p[i].a);
    sort(p+1,p+1+n);
    for(int i=1;i<=n;i++){
        if(p[i].s<d) continue;
        ++ans;
        d=max(d,p[i].a);
    }
    printf("%d\n",ans);
    return 0;
}
posted @ 2025-08-06 15:54  headless_piston  阅读(23)  评论(2)    收藏  举报