集训总结

集训总结

集训过了得有 \(\dfrac{3}{4}\) 多了,模拟赛也基本打完了,感觉好像是学了不少,又好像什么也没学,自认为是尽力了吧。

知识点总结:

数据结构:

整体而言,代码能力应该是有所提升的。

  1. 平衡树:

    这应该是我进步的最大的了吧,也在一直调这里的题 虽然都是板子就是了

    基本掌握了单点的 treapfhq_treap,知道了 splay 的原理(虽然还没打过),区间的fhq_treap 正在调,应该可以在回家前完成。

  2. 线段树:

    本来就会,也没啥好说的。

  3. 分块、莫队:

    分块知道思路,也没来的及写题,莫队就是根本就不会了。

  4. 可持久化:

    会思路吧。

DP:

马上要讲,讲完再补吧。

数学:

算是仔细听了一下,虽然是复习,难度还是不低。

竟然是听会了容斥,还是出乎意料的,但只是看了例题,算半会吧,顺带也就半会了二项式反演,做例题的时候感觉挺实用的。

排列组合(和一些定理如 exgcd 、费马小定理)算是简单复习了一下,排列式子还是没背过(不会灵活用

简单听了一下莫比乌斯反演,基本就听懂一个定义和式子(但好像也没什么其他的了),应用还够呛。

欧拉函数(反演)也算是听的听好的了(相较于莫反),应用应该也可以试试,但整体的做题量远不够。

其他的像线性筛之类的倒是差不多 除了线性筛也没有啥了

至于更难的计算几何,就是一点也不会,后面索性就不听了。

其他:

树的重心、直径算是补上了我之前的短板。

数字哈希是学会了,字符串哈希还是差点,没怎么做题。

二分、三分、分治都差不多(好像这种东西还是难在思路,算法很简单),CDQ 套树状数组(或 CDQ 套 CDQ 套 CDQ ……)实在是迷惑,希望可以最少把求解三元逆元学会。

搜索进阶如 A* 等好像还不错(毕竟是第二遍听

题目总结(不保证链接能打开:

  1. 下棋

    真的是不难,单纯是枚举每一个点,在判断是否可以到达这个点,但这个思路实现还值得一看吧(对我来说

    CODE
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long llt;
    const int N=2004;
    #define For(i,a,b,c) for(int $L=a,$R=b,$C=c,$D=(0<=$C)-($C<0),i=$L;i*$D<=$R*$D;i+=$C)
    int n,m;
    char c[N][N];
    bool vis[N][N];
    
    namespace IO{
        template<typename T> inline void read(T &x){
            char s=getchar();x=0;bool pd=false;
            while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();}
            while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
            if(pd) x=-x;
        }
        template<typename T,typename... Args> inline void read(T& x,Args&... args){read(x);read(args...);}
        template<typename T> inline void write(T x){
            static T st[45];T top=0;
            if(x<0)x=~x+1,putchar('-');
            do{st[top++]=x%10;}while(x/=10);
            while(top)putchar(st[--top]^48);
        }
        inline void write(const char c){putchar(c);}
        inline void write(const char *c){
            int len=strlen(c);
            For(i,0,len-1,1) putchar(c[i]);
        }
        template<typename T> inline void Write(T x){write(x);putchar(' ');}
        inline void Write(const char c){write(c);if(c!='\n') putchar(' ');}
        inline void Write(const char *c){write(c),putchar(' ');}
        template<typename T,typename... Args> inline void write(T x,Args... args){write(x);write(args...);}
        template<typename T,typename... Args> inline void Write(T x,Args... args){Write(x);Write(args...);}
    }
    #define read IO::read
    #define write IO::write
    #define Write IO::Write
    
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("in.in","r",stdin);
        freopen("out.out","w",stdout);
    #endif
        read(n,m);
        memset(c,'z',sizeof c);
        For(i,1,n,1) scanf(" %s",c[i]+1);
        vis[1][0]=vis[0][1]=1;
        For(l,2,n+m-1,1){
            char x='z';
            For(i,1,n,1){
                int j=l-i;
                if(1<=j&&j<=m)
                    if(vis[i][j-1]||vis[i-1][j]) x=min(x,c[i][j]);
            }
            putchar(x);
            For(i,1,n,1){
                int j=l-i;
                if(1<=j&&j<=m)
                    if((vis[i][j-1]||vis[i-1][j])&&c[i][j]==x)
                        vis[i][j]=1;
            }
        }
        putchar(c[n][m]);
    }
    
  2. 分钱

    一个巧妙 \(dp\) ,考虑 \(dp_{i,j}\) 表示选前 \(i\) 张纸币,差为 \(j\) 时的每个人的最大钱数。

    有一个显然转移:

    对于第 \(k\) 张纸币,其价值为 \(a_k\)

    \[dp_{i,j}=\max\begin{cases}dp_{i-1,j}\\dp_{i-1,j+a_k}+a_k\\dp_{i-1,\mid j-a_k\mid}+a_k\end{cases} \]

    分别对应的是将第 \(k\) 张给多的人,少的人,不给人。

    CODE
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long llt;
    const int N=504,M=1e5+3;
    #define For(i,a,b,c) for(int $L=a,$R=b,$C=c,$D=(0<=$C)-($C<0),i=$L;i*$D<=$R*$D;i+=$C)
    int n,w[N],dp[N][M],sum;
    
    namespace IO{
        template<typename T> inline void read(T &x){
            char s=getchar();x=0;bool pd=false;
            while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();}
            while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
            if(pd) x=-x;
        }
        template<typename T,typename... Args> inline void read(T& x,Args&... args){read(x);read(args...);}
        template<typename T> inline void write(T x){
            static T st[45];T top=0;
            if(x<0)x=~x+1,putchar('-');
            do{st[top++]=x%10;}while(x/=10);
            while(top)putchar(st[--top]^48);
        }
        inline void write(const char c){putchar(c);}
        inline void write(const char *c){
            int len=strlen(c);
            For(i,0,len-1,1) putchar(c[i]);
        }
        template<typename T> inline void Write(T x){write(x);putchar(' ');}
        inline void Write(const char c){write(c);if(c!='\n') putchar(' ');}
        inline void Write(const char *c){write(c),putchar(' ');}
        template<typename T,typename... Args> inline void write(T x,Args... args){write(x);write(args...);}
        template<typename T,typename... Args> inline void Write(T x,Args... args){Write(x);Write(args...);}
    }
    #define read IO::read
    #define write IO::write
    #define Write IO::Write
    
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("in.in","r",stdin);
        freopen("out.out","w",stdout);
    #endif
        read(n);
        for(int i=1;i<=n;i++) read(w[i]),sum+=w[i];
        memset(dp,-0x7f,sizeof dp);
        dp[0][0]=0;
        for(int i=1;i<=n;i++)
            for(int j=0;j<=sum;j++)
                dp[i][j]=max(dp[i-1][j],max(dp[i-1][abs(j-w[i])],dp[i-1][j+w[i]])+w[i]);
        write(sum-(dp[n][0]/2));
    }
    
  3. Super_Jaber

    01bfs(好像也有叫其他名字的。

    反正我之前是不会,稍微学了一下。

    用来求解在所有节点编号是 \(0\)\(1\) 的网格图中,所有 \(0\)\(1\) 的最短路径。具体实现就是先将 \(1\) 加入队列,然后扩展(类似不用优先队列的 dij。

    这道题还有一个瞬移,转移时加上即可

    CODE
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long llt;
    const int N=1010,K=50;
    #define For(i,a,b,c) for(int $L=a,$R=b,$C=c,$D=(0<=$C)-($C<0),i=$L;i*$D<=$R*$D;i+=$C)
    #define chpt(x,y) (1<=x&&x<=n&&1<=y&&y<=m&&dis[cl][x][y]<0)
    int n,m,k,q,ca[N][N],dis[K][N][N],vis[N];
    struct pt{int x,y;}que[N];
    vector<pt> cc[K];
    
    namespace IO{
        template<typename T> inline void read(T &x){
            char s=getchar();x=0;bool pd=false;
            while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();}
            while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
            if(pd) x=-x;
        }
        template<typename T,typename... Args> inline void read(T& x,Args&... args){read(x);read(args...);}
        template<typename T> inline void write(T x){
            static T st[45];T top=0;
            if(x<0)x=~x+1,putchar('-');
            do{st[top++]=x%10;}while(x/=10);
            while(top)putchar(st[--top]^48);
        }
        inline void write(const char c){putchar(c);}
        inline void write(const char *c){
            int len=strlen(c);
            For(i,0,len-1,1) putchar(c[i]);
        }
        template<typename T> inline void Write(T x){write(x);putchar(' ');}
        inline void Write(const char c){write(c);if(c!='\n') putchar(' ');}
        inline void Write(const char *c){write(c),putchar(' ');}
        template<typename T,typename... Args> inline void write(T x,Args... args){write(x);write(args...);}
        template<typename T,typename... Args> inline void Write(T x,Args... args){Write(x);Write(args...);}
    }
    #define read IO::read
    #define write IO::write
    #define Write IO::Write
    
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("in.in","r",stdin);
        freopen("out.out","w",stdout);
    #endif
        memset(dis,-1,sizeof dis);
        read(n,m,k);
        For(i,1,n,1) For(j,1,m,1) read(ca[i][j]),cc[ca[i][j]].push_back({i,j});
        For(cl,1,k,1){
            queue<pt> que;
            for(pt i:cc[cl]) que.push(i),dis[cl][i.x][i.y]=0;
            while(!que.empty()){
                int x=que.front().x,y=que.front().y;que.pop();
                int col=ca[x][y],cld=dis[cl][x][y];
                if(chpt(x+1,y)) dis[cl][x+1][y]=dis[cl][x][y]+1,que.push({x+1,y});//转移移动
                if(chpt(x-1,y)) dis[cl][x-1][y]=dis[cl][x][y]+1,que.push({x-1,y});
                if(chpt(x,y+1)) dis[cl][x][y+1]=dis[cl][x][y]+1,que.push({x,y+1});
                if(chpt(x,y-1)) dis[cl][x][y-1]=dis[cl][x][y]+1,que.push({x,y-1});
                if(vis[col]!=cl){
                    vis[col]=cl;
                    for(pt i:cc[col])
                        if(dis[cl][i.x][i.y]<0) dis[cl][i.x][i.y]=cld+1,que.push({i.x,i.y});//转移颜色
                }
            }
        }
        read(q);
        For(i,1,q,1){
            int ax,ay,bx,by;read(ax,ay,bx,by);
            int mi=abs(ax-bx)+abs(ay-by);
            For(j,1,k,1) mi=min(mi,dis[j][ax][ay]+dis[j][bx][by]+1);
            write(mi,'\n');
        }
    }
    
  4. 糖果

    考虑一个如果存在一个合法分法,一定可以将其分成 \(2,3\) 组(因为每个异或和一样的 \(3\) 组就可以合并成一组。

    对于 \(2\) 组,直接全局前缀异或和为 \(0\)

    对于 \(3\) 组,可以枚举前缀,在在后缀中查找是否相等,在判一下全部的异或和是否相等,

  5. 魔法仪式

    经典区间分治。

    考虑将中点左右分治后合并。

    将最大值在左右两边分别考虑。

    有中点到左边枚举点作为最大的值,指针右边的可取范围(类似双指针),在同时维护 \(sum_x\) 表示到右边指针范围内 和 \(\bmod k=x\) 的个数。

    右边同理维护。

其他想说的:

写这个的初心是模拟赛打的太寄了,想着先把东西总结一下在学新的。

accoders 上的题还是挺好的(都不会。

真说收获,也不少了,也感受到了自己的成长和不足。

应该还会补吧。

posted @ 2023-08-09 09:24  xrlong  阅读(22)  评论(0编辑  收藏  举报