集训总结
集训总结
集训过了得有 \(\dfrac{3}{4}\) 多了,模拟赛也基本打完了,感觉好像是学了不少,又好像什么也没学,自认为是尽力了吧。
知识点总结:
数据结构:
整体而言,代码能力应该是有所提升的。
-
平衡树:
这应该是我进步的最大的了吧,也在一直调这里的题
虽然都是板子就是了。基本掌握了单点的
treap
,fhq_treap
,知道了splay
的原理(虽然还没打过),区间的fhq_treap
正在调,应该可以在回家前完成。 -
线段树:
本来就会,也没啥好说的。
-
分块、莫队:
分块知道思路,也没来的及写题,莫队就是根本就不会了。
-
可持久化:
会思路吧。
DP:
马上要讲,讲完再补吧。
数学:
算是仔细听了一下,虽然是复习,难度还是不低。
竟然是听会了容斥,还是出乎意料的,但只是看了例题,算半会吧,顺带也就半会了二项式反演,做例题的时候感觉挺实用的。
排列组合(和一些定理如 exgcd 、费马小定理)算是简单复习了一下,排列式子还是没背过(不会灵活用
简单听了一下莫比乌斯反演,基本就听懂一个定义和式子(但好像也没什么其他的了),应用还够呛。
欧拉函数(反演)也算是听的听好的了(相较于莫反),应用应该也可以试试,但整体的做题量远不够。
其他的像线性筛之类的倒是差不多 除了线性筛也没有啥了。
至于更难的计算几何,就是一点也不会,后面索性就不听了。
其他:
树的重心、直径算是补上了我之前的短板。
数字哈希是学会了,字符串哈希还是差点,没怎么做题。
二分、三分、分治都差不多(好像这种东西还是难在思路,算法很简单),CDQ 套树状数组(或 CDQ 套 CDQ 套 CDQ ……)实在是迷惑,希望可以最少把求解三元逆元学会。
搜索进阶如 A*
等好像还不错(毕竟是第二遍听
题目总结(不保证链接能打开:
-
下棋:
真的是不难,单纯是枚举每一个点,在判断是否可以到达这个点,但这个思路实现还值得一看吧(对我来说
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]); }
-
分钱:
一个巧妙 \(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)); }
-
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'); } }
-
考虑一个如果存在一个合法分法,一定可以将其分成 \(2,3\) 组(因为每个异或和一样的 \(3\) 组就可以合并成一组。
对于 \(2\) 组,直接全局前缀异或和为 \(0\)。
对于 \(3\) 组,可以枚举前缀,在在后缀中查找是否相等,在判一下全部的异或和是否相等,
-
经典区间分治。
考虑将中点左右分治后合并。
将最大值在左右两边分别考虑。
有中点到左边枚举点作为最大的值,指针右边的可取范围(类似双指针),在同时维护 \(sum_x\) 表示到右边指针范围内 和 \(\bmod k=x\) 的个数。
右边同理维护。
其他想说的:
写这个的初心是模拟赛打的太寄了,想着先把东西总结一下在学新的。
accoders 上的题还是挺好的(都不会。
真说收获,也不少了,也感受到了自己的成长和不足。
应该还会补吧。
本文来自博客园,作者:xrlong,转载请注明原文链接:https://www.cnblogs.com/xrlong/articles/17615862.html