【考试反思】联赛模拟测试10

这波啊,这波是直接倒一

考数据结构必挂选手

四道原题,两道没改,一道没做过,其中 T1 当时过了这次却挂了 10 分,这都是需要反思的。

注 意 爆 零

T1:100 \(\rightarrow\) 90

T4:30 \(\rightarrow\) 5

T1:凉宫春日的忧郁

算法1:取对数

\(X^Y\)\(Y!\) 取对数,前者是 \(Y\log X\),后者是 \(\log1+\log2+\cdots+\log Y\),可以直接比较大小,记得防止炸精。

算法2:乱搞

可以发现分界线大概是 \(\cfrac{X}{Y}=\cfrac{2}{5}\)。直接判断即可。不过要注意在小数据是不满足这个规律的,所以小数据需要暴力,否则会像我一样挂 \(10\ \text{pts}\)

代码不粘了,有手就行。

T2:漫无止境的八月

可以发现,只有在所有模 \(k\) 意义下同余的位置的和都相等的时候游戏才能获胜。所以开哈希表当桶存一下就好了。

出题人丧心病狂居然连 unordered_map 都卡。人傻了。

记得开 long long

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e6+10;
const int Mod=1152763;
int n,K,q;
int a[maxn],sum[maxn];

struct Node{
    int to,w,nxt;
}e[maxn<<1];

inline int read(){
    int x=0;bool fopt=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
    for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
    return fopt?x:-x;
}

int head[Mod+10],cnt;
inline void push(int u,int v){
    e[++cnt].to=v;
    e[cnt].w=1;
    e[cnt].nxt=head[u];
    head[u]=cnt;
}

inline void add(int u){
    int x=u%Mod;
    for(int i=head[x];i;i=e[i].nxt)
        if(e[i].to==u)return e[i].w++,void();
    push(x,u);
}

inline void del(int u){
    int x=u%Mod;
    for(int i=head[x];i;i=e[i].nxt)
        if(e[i].to==u)return e[i].w--,void();
    throw;
}

inline int query(int u){
    int x=u%Mod;
    for(int i=head[x];i;i=e[i].nxt)
        if(e[i].to==u)return e[i].w;
    return 0;
}

signed main(){
#ifndef LOCAL
    freopen("august.in","r",stdin);
    freopen("august.out","w",stdout);
#endif
    n=read();K=read();q=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
        sum[i%K]+=a[i];
    }
    for(int i=0;i<K;i++)
        add(sum[i]);
    puts(query(sum[0])==K?"Yes":"No");
    //实际上如果符合条件的话所有的sum值都是相等的,但只有sum[0]一定存在(k=n=1),所以查询sum[0]
    while(q--){
        int pos=read(),dx=read();
        del(sum[pos%K]);
        sum[pos%K]+=dx;
        add(sum[pos%K]);
        puts(query(sum[0])==K?"Yes":"No");
    }
    return 0;
}

另一种思想是利用差分,就不用开桶了,还可以防止炸内存。通通的题解

T3:射手座之日

关于 dfs 序的部分,只要计算子节点相互之间的 \(size\) 乘积乘上当前点的权值即可。

正解是 dsu on tree,用两个数组维护当前点是否是一个极长区间的左右端点,同时需要记录另一个端点的位置,一边合并一边统计方案数。

一般 dsu on tree 的题都能用线段树合并,本题也相同。时间复杂度都是 \(O(n\log n)\) 的。

动动大神的代码

T4:货车运输

是 NOIP 2013 的原题。

首先对于两个点的若干条简单路径中,一定要选路径中权值最小的边最大的一条路径。所以我们能想到最大生成树。

当把最大生成树建出来之后,直接用树链剖分+线段树求路径上的最小值就可以了。

依然需要开 long long

事实上,用树剖的时间复杂度是小常数 \(O(n\log^2 n)\),但足以通过本题。如果使用倍增可以做到 \(O(n\log n)\),而且代码短很多。但是我不想写倍增,因为三个 namespace 很带感。

#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
const int INF=0x3f3f3f3f;
int n,m;
vector<pair<int,int> > g[maxn];

struct Edge{
    int from,to,w;
    friend inline bool operator <(register const Edge& A,register const Edge& B){
        return A.w>B.w;
    }
}e[maxn];

inline int read(){
    int x=0;bool fopt=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
    for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
    return fopt?x:-x;
}

namespace DSU{
    int f[maxn];
    inline void Init(){
        for(int i=1;i<=n;i++)
            f[i]=i;
    }
    
    int Find(int x){
        return x==f[x]?x:(f[x]=Find(f[x]));
    }
    
    inline bool isLink(int u,int v){
        return Find(u)==Find(v);
    }
    
    inline void Merge(int u,int v){
        int fau=Find(u),fav=Find(v);
        if(fau!=fav)f[fau]=fav;
    }
}
using namespace DSU;

inline void Kruskal(){
    sort(e+1,e+m+1);
    int tot=0;
    for(int i=1;i<=m;i++){
        if(tot==n-1)break;
        int u=e[i].from,v=e[i].to,w=e[i].w;
        if(!isLink(u,v)){
            Merge(u,v);tot++;
            g[u].push_back(make_pair(v,w));
            g[v].push_back(make_pair(u,w));
        }
    }
}

namespace LinkCut{
    int fa[maxn],dep[maxn],siz[maxn],son[maxn],val[maxn];
    void dfs1(int u){
        dep[u]=dep[fa[u]]+1;siz[u]=1;
        for(int i=0;i<g[u].size();i++){
            int v=g[u][i].first;
            if(v==fa[u])continue;
            fa[v]=u;val[v]=g[u][i].second;
            dfs1(v);
            siz[u]+=siz[v];
            if(!son[u]||siz[v]>siz[son[u]])son[u]=v;
        }
    }

    int Nodetot;
    int top[maxn],dfn[maxn],rank[maxn];
    void dfs2(int u,int t){
        top[u]=t;dfn[u]=++Nodetot;rank[Nodetot]=u;
        if(son[u])dfs2(son[u],t);//注意一定要先递归重儿子!
        for(int i=0;i<g[u].size();i++){
            int v=g[u][i].first;
            if(v==fa[u]||v==son[u])continue;
            dfs2(v,v);
        }
    }
}
using namespace LinkCut;

namespace SEG{
#define lson (rt<<1)
#define rson (rt<<1|1)

    int tree[maxn<<2];
    inline void pushup(int rt){
        tree[rt]=min(tree[lson],tree[rson]);
    }

    void build(int rt,int l,int r){
        if(l==r)return tree[rt]=val[rank[l]],void();
        int mid=(l+r)>>1;
        build(lson,l,mid);build(rson,mid+1,r);
        pushup(rt);
    }

    int query(int rt,int l,int r,int s,int t){
        if(r<l)return INF;
        if(s<=l&&r<=t)return tree[rt];
        int mid=(l+r)>>1,ans=INF;
        if(s<=mid)ans=min(ans,query(lson,l,mid,s,t));
        if(t>mid)ans=min(ans,query(rson,mid+1,r,s,t));
        return ans;
    }

    inline int querymin(int u,int v){
        int ans=INF;
        while(top[u]!=top[v]){
            if(dep[top[u]]<dep[top[v]])swap(u,v);
            ans=min(ans,query(1,1,n,dfn[top[u]],dfn[u]));
            u=fa[top[u]];
        }
        if(dep[u]<dep[v])swap(u,v);
        ans=min(ans,query(1,1,n,dfn[v]+1,dfn[u]));
        return ans;
    }

#undef lson
#undef rson
}
using namespace SEG;

int main(){
#ifndef LOCAL
    freopen("truck.in","r",stdin);
    freopen("truck.out","w",stdout);
#endif
    n=read();m=read();Init();
    for(int i=1;i<=m;i++){
        int u=read(),v=read(),w=read();
        e[i]=(Edge){u,v,w};
    }
    Kruskal();dfs1(1);dfs2(1,1);build(1,1,n);
    int T=read();
    while(T--){
        int u=read(),v=read();
        if(!isLink(u,v))puts("-1");
        else printf("%d\n",querymin(u,v));
    }
    return 0;
}

T5:作业

提供一个非正解。这道题在题库里的时限卡到了 5s 导致会 TLE 两个点,在洛谷和 BZOJ 上是可以 A 的。

其实也没啥,暴力一看就是莫队+权值树状数组作桶。时间复杂度 \(O(m\sqrt{n\log n})\)。第一问有手就行,第二问就是 HH的项链 改一改。

%:pragma GCC target("avx")
%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast","inline","-ffast-math")
%:pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int n,m,S;
int c[maxn];

struct Node{
    int l,r,a,b,id;
    friend inline bool operator <(register const Node& A,register const Node& B){
        return (A.l/S)^(B.l/S)?A.l<B.l:(((A.l/S)&1)?A.r<B.r:A.r>B.r);
    }
}q[maxn];

char buf[1<<20],*p1,*p2;
#define gc() (p1==p2?(p2=buf+fread(p1=buf,1,1<<20,stdin),p1==p2?EOF:*p1++):*p1++)
inline int read(){
    int x=0;bool fopt=1;char ch=gc();
    for(;!isdigit(ch);ch=gc())if(ch=='-')fopt=0;
    for(;isdigit(ch);ch=gc())x=(x<<3)+(x<<1)+ch-48;
    return fopt?x:-x;
}

int tree[2][maxn];
#define lowbit(x) (x&-x)

inline void add(int x,int b,int opt){
    while(x<=n){
        tree[opt][x]+=b;
        x+=lowbit(x);
    }
}

inline int query(int x,int opt){
    int ret=0;
    while(x){
        ret+=tree[opt][x];
        x-=lowbit(x);
    }
    return ret;
}

int cnt[maxn];
pair<int,int> res[maxn];
inline void moadd(int x){
    add(c[x],1,0);
    if(!cnt[c[x]])add(c[x],1,1);
    cnt[c[x]]++;
}

inline void model(int x){
    add(c[x],-1,0);
    cnt[c[x]]--;
    if(!cnt[c[x]])add(c[x],-1,1);
}

int main(){
#ifndef LOCAL
    freopen("homework.in","r",stdin);
    freopen("homework.out","w",stdout);
#endif
    n=read();m=read();
    for(int i=1;i<=n;i++)
        c[i]=read();
    S=sqrt(n);
    for(int i=1;i<=m;i++){
        int l=read(),r=read(),a=read(),b=read();
        q[i]=(Node){l,r,a,b,i};
    }
    sort(q+1,q+m+1);
    int l=1,r=0;
    for(int i=1;i<=m;i++){
        int s=q[i].l,t=q[i].r,a=q[i].a,b=q[i].b;
        while(r<t)moadd(++r);
        while(l>s)moadd(--l);
        while(r>t)model(r--);
        while(l<s)model(l++);
        res[q[i].id].first=query(b,0)-query(a-1,0);
        res[q[i].id].second=query(b,1)-query(a-1,1);
    }
    for(int i=1;i<=m;i++)
        printf("%d %d\n",res[i].first,res[i].second);
    return 0;
}
posted @ 2020-10-06 17:40  Midoria7  阅读(177)  评论(2编辑  收藏  举报