2018暑假集训记录(一)
暑假集训个人日常(好熟悉的开场🙊)。
day1 06.30
Contest:The 2015 ACM-ICPC ChangChun Regional Contest
[🎈]
A Too Rich(贪心)[ ] B Count a * b
[ ] C Play a game
[ ] D Pipes selection
[ ] E Rebuild
[🎈] F Almost Sorted Array(递推)
[🎈] G Dancing Stars on Me(计算几何凸包)
[🎈]
H Partial Tree(完全背包)[ ] I Chess Puzzle
[🎈] J Chip Factory(暴力/01Trie树)
[ ] K Maximum Spanning Forest
[🎈] L House Building(简单计算)
[ ] M Security Corporation
EveningTraining
[🎈] A Most Powerful
[ ] B Food Delivery
[ ] C Rugby Football
[ ] D Dice War
[ ] E Magic SquaresMumbles
G读完题目不看样例于是就理解错了… J读完题目算算复杂度不敢莽于是隔壁就A了… F边界不好好处理于是1A就没了… 去年day4的A题我写了个恶心的优先队列然后今天写了个优美的线段树还在沾沾自喜然后发现其实只要扫一遍预处理就over了… 滚粗👋。
Solutions
【A】数据范围太大不能背包,但是这题比普通背包更巧妙的条件在于,除了$50$和$500$,其余硬币的价值都是前一个的整数倍,也就是说可以用若干个硬币$i$代替一个硬币$i+1$,所以我们在枚举的时候如果考虑多取一个$50/500$凑成$100/1000$,那就可以保证贪心的正确性。贪心策略是,用尽可能多的小面值硬币,前提是小面值硬币可以凑出当前需要的钱数,所以从大面值的开始决策。
123456789101112131415161718192021222324252627282930313233343536373839404142434445using namespace std;const int maxn=15;int T, p, ans, c[maxn], val[maxn]={0, 1, 5, 10, 20, 50, 100, 200, 500, 1000, 2000}, sum[maxn];void Dfs(int tot, int id, int cnt){if(tot<0) return ;if(id==0){if(tot==0) ans=max(ans, cnt);return ;}int ret=max(tot-sum[id-1], 0);int tmp=ret/val[id]+(ret%val[id]!=0);if(tmp<=c[id]) Dfs(tot-tmp*val[id], id-1, cnt+tmp);if(tmp+1<=c[id]) Dfs(tot-(tmp+1)*val[id], id-1, cnt+tmp+1);return ;}int main(){scanf("%d", &T);while(T--){scanf("%d", &p);for(int i=1; i<=10; i++){scanf("%d", &c[i]);sum[i]=c[i]*val[i]+sum[i-1];}ans=-1;Dfs(p, 10, 0);printf("%d\n", ans);}return 0;}【H】因为$n$个点的树总度数确定为$2n-2$,一个很直接的想法是做完全背包,但最简单的背包不能保证连通。我们可以考虑先把每个点分配$1$度,提前保证连通,剩下的$n-2$度再进行完全背包,因为每次分配相当于把一个点的从$1$度变成了$k$度,所以增加的贡献应该是$a[1]-a[k]$(注意$f$数组不能初始化为-1,因为这里有可能为负数= =),最终答案为$f[n-2]+n*a[1]$。
123456789101112131415161718192021222324252627282930using namespace std;const int maxn=2500+10;int T, n, m, a[maxn], f[maxn];int main(){scanf("%d", &T);while(T--){scanf("%d", &n);for(int i=1; i<n; i++) scanf("%d", &a[i]);memset(f, 0x8f, sizeof f);f[0]=0;for(int i=2; i<n; i++)for(int j=i-1; j<=n-2; j++)f[j]=max(f[j], f[j-i+1]+a[i]-a[1]);printf("%d\n", f[n-2]+n*a[1]);}return 0;}【J】暴力水,Trie做法后补…
day2 07.01
Contest:The 2015 ACM-ICPC Shenyang Regional Contest
[ ] A Pattern String
[🎈] B Bazinga(字符串)
[ ] C Minimum Cut-Cut
[🎈] D Pagodas(数学)
[ ] E Efficient Tree
[ ] F Frogs
[🎈]
G Game of Flying Circus(模拟)[ ] H Chessboard
[ ] I Triple
[ ] J John’s Fences
[ ] K Kykneion asma
[ ] L Number Link
[🎈] M Meeting(拆点最短路)
EveningTraining
[ ] A GT and sequence
[ ] B GT and numbers
[ ] C GT and set
[ ] D GT and strings
[ ] E GT and treesMumbles
一个人做比赛真是菜出天际,其实也是反映出自己有多菜吧= = 折腾了一下午比赛日程和hexo的配置,interesting!
Solutions
【B】暴力一定会T,但是题目的要求是从后往前找到第一个不能覆盖前面所有串的串,很巧妙。我们定义$s[i]\subseteq s[j]$代表$s[i]$是$s[j]$的子串,假设现在枚举到$s[i]$和$s[j]$,如果$s[i]\nsubseteq s[j]$,那么$s[j]$满足条件;否则,对于更往后的$s[k]$串,如果$s[j]\nsubseteq s[k]$的子串那$s[k]$满足条件,否则$s[j]\subseteq s[k]$时必有$s[i]\subseteq s[k]$,那么$s[i]$对$s[k]$的所有影响都可以由$s[j]$代替,则可以跳出$j$这层循环。偷懒没敲kmp用了一下strstr这个函数,后来查了一下看到这个博客里提到它的效率很优秀。
1234567891011121314151617181920212223242526272829303132333435363738using namespace std;const int maxn=500+10;int T, n;char s[maxn][2000+10];bool vis[maxn];int main(){scanf("%d", &T);for(int tt=1; tt<=T; tt++){scanf("%d", &n);for(int i=1; i<=n; i++) scanf("%s", s[i]);int ans=-1;memset(vis, 0, sizeof vis);for(int i=1; i<=n; i++)for(int j=i+1; j<=n; j++) if(!vis[j]){if(strstr(s[j], s[i])) break;else{vis[j]=true;ans=max(ans, j);}}printf("Case #%d: %d\n", tt, ans);}return 0;}【D】想到了去年的省赛= = 每次把最小的两个数作差生成新数,容易发现能得到的最小数就是$gcd(a, b)$,再把$gcd$加在这些数上面生成新数,其实到最后这就是一个等差数列。
【M】把集合E拆成两个点$S_E$和$T_E$,对集合内的每个点$P$,分别连边$
$和$
$,然后连边$ $,建图之后跑单源最短路(突然发现好像不拆点只修改一下边权也是可以的…)。分别求出每个点到$1$和$n$的最短距离,求这两个距离中的最大值的最小值,当时找答案的时候完全在乱写,鬼知道我在想些什么🤦🏻♀️。 【G】本来就只是一个题意很恶心的模拟题,结果自己推导的过程太复杂反而把思路搞混乱了,写的时候还搞错一个条件= = 据说这个题目有一点错误,这里有完整题意。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657using namespace std;const double def=1e-6;int T;double t, va, vs;char s[2][5]={"No", "Yes"};int main(){scanf("%d", &T);for(int tt=1; tt<=T; tt++){scanf("%lf%lf%lf", &t, &va, &vs);bool flag=false;if(fabs(va-vs)<=def) flag=true;else{double len=300.0, line=sqrt(2.0)*300.0, vsub=vs*vs-va*va;if(line/va < 2*len/vs){double delta=(2*vs*len)*(2*vs*len)-4*vsub*(2*len*len), x;if(delta>=0){x=(2*vs*len-sqrt(delta))/(2*vsub)*vs-len;if(x>=0 && x<len && (x+2*len)/va-t <= (2*len-x)/vs) flag=true;x=(2*vs*len+sqrt(delta))/(2*vsub)*vs-len;if(x>=0 && x<len && (x+2*len)/va-t <= (2*len-x)/vs) flag=true;}}if(len/va < 3*len/vs){double delta=(6*vs*len)*(6*vs*len)-4*vsub*(10*len*len), x;if(delta>=0){x=(6*vs*len-sqrt(delta))/(2*vsub)*vs-2*len;if(x>=0 && x<=len && (sqrt(x*x+len*len)+3*len)/va-t <= (2*len-x)/vs) flag=true;x=(6*vs*len+sqrt(delta))/(2*vsub)*vs-2*len;if(x>=0 && x<=len && (sqrt(x*x+len*len)+3*len)/va-t <= (2*len-x)/vs) flag=true;}}}printf("Case #%d: %s\n", tt, s[flag]);}return 0;}
day3 07.02
Contest:The 2015 ACM-ICPC Shanghai Regional Contest
[🎈]
A An Easy Physics Problem[ ] B Binary Tree
[ ] C Colorful Tree
[ ] D Discover Water Tank
[ ] E Expection of String
[🎈] F Friendship of Frog
[ ] G Game of Arrays
[ ] H Happiness of Frog
[ ] I Infinity Point Sets
[ ] J Journey of Taku
[🎈] K Kingdom of Black and White
[🎈] L LCM Walk
Mumbles
一觉醒来看到鱼头真是吓死我了= =下午搞计算几何去吧。祭奠死去的A。
Solutions
【L】对于$(x,y)$,必有$x, y\le lcm(x, y)$,显然$x$和$y$中更大的那个数是由上一步变换而来,那么对于当前坐标它的前驱是唯一的。设$x<y$,令$k=gcd(x,y), x=ak,y=bk,lcm(x,y)=abk$,若下一步得到$(x, y+lcm(x,y))=(ak, bk+abk)=(ak,(ak+k)b)=(x,(x+k)b)$,那此时$y_1\%(x+k)==0$且此时$gcd(x, y1)$仍然为$k$;由$x$变换到$x_1$同理。所以只需要不断将$x$和$y$往前变换直到不满足$y\%(x+k)==0$为止。
123456789101112131415161718192021222324252627282930313233using namespace std;int T, x, y;int gcd(int a, int b){if(!b) return a;return gcd(b, a%b);}int main(){scanf("%d", &T);for(int tt=1, x, y; tt<=T; tt++){scanf("%d%d", &x, &y);int cnt=1, k=gcd(x, y);if(x>y) swap(x, y);while(y%(x+k)==0){++cnt, y=y/(x+k)*k;if(x>y) swap(x, y);}printf("Case #%d: %d\n", tt, cnt);}return 0;}【A】终于写完这个计算几何,论好模版的必要性。感觉整个人都明媚了,刷题刷题gogogo🤣。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227using namespace std;const double eps=1e-5, PI=acos(-1.0);int sgn(double d) {if(fabs(d)<eps) return 0;return d>0 ? 1 : -1;}struct Point{double x, y;Point(double _x=0, double _y=0) {x=_x, y=_y;}Point operator +(const Point&b)const {return Point(x+b.x, y+b.y);}Point operator -(const Point&b)const {return Point(x-b.x, y-b.y);}Point operator *(const double&b)const {return Point(x*b, y*b);}Point operator /(const double&b)const {return Point(x/b, y/b);}double operator *(const Point&b)const {//点积,向量a*b=|a||b|cos(angle),得到a在b上的投影长度//a*b>0则夹角小于90度,a*b=0则向量垂直,a*b<0则夹角大于90度return x*b.x+y*b.y;}double operator ^(const Point&b)const {//叉积,得到垂直于ab平面的向量,返回其长度//a^b>0则a在b顺时针方向,a^b<0则a在b逆时针方向,a^b=0则a、b共线(方向不定)return x*b.y-y*b.x;}void transXY(double B) {//求绕原点旋转B(弧度值)后的点double tx=x, ty=y;x=tx*cos(B)-ty*sin(B);y=tx*sin(B)+ty*cos(B);}double len2() {return x*x+y*y;}double len() {return sqrt(len2());}Point change_len(double r) {//转化为长度为r的向量double l=len();if(sgn(l)==0) return *this;return Point(x*r/l, y*r/l);}void sc(){scanf("%lf%lf", &x, &y);return ;}void pr(){printf("%.2f %.2f\n", x, y);}};struct Line {Point s, e;double a, b, c, angle;Line(){}Line(Point _s, Point _e) {s=_s, e=_e;a=s.y-e.y, b=e.x-s.x, c=s.x*e.y-e.x*s.y;angle=atan2(e.y-s.y, e.x-s.x);}};typedef Point Vec;typedef Line Seg;bool pointOnSeg(Point P, Line L) {//判断点在线段上returnsgn((L.s-P)^(L.e-P))==0 &&sgn((P.x-L.s.x)*(P.x-L.e.x))<=0 &&sgn((P.y-L.s.y)*(P.y-L.e.y))<=0;}bool pointCollinear(Point P, Line L) {//判断点在直线上,即三点共线return sgn((L.s-P)^(L.e-P))==0;}int interL2L(Line l1, Line l2) {//判断直线关系,0重合,1平行,2相交if(sgn((l1.s-l1.e)^(l2.s-l2.e))==0) {if(sgn((l1.s-l2.e)^(l2.s-l2.e))==0) return 0;else return 1;}else return 2;}int dirV2V(Vec a, Vec b) {//判断向量方向,0垂直,1同向,2反向,3其他if(sgn(a*b)==0) return 0;else if(sgn(a*b-a.len()*b.len())==0) {if(sgn(a*b)>0) return 1;else return 2;}else return 3;}bool interS2S(Seg l1, Seg l2) {//判断线段是否相交returnmax(l1.s.x, l1.e.x)>=min(l2.s.x, l2.e.x) &&max(l2.s.x, l2.e.x)>=min(l1.s.x, l1.e.x) &&max(l1.s.y, l1.e.y)>=min(l2.s.y, l2.e.y) &&max(l2.s.y, l2.e.y)>=min(l1.s.y, l1.e.y) &&sgn((l2.s-l1.e)^(l1.s-l1.e))*sgn((l2.e-l1.e)^(l1.s-l1.e))<=0 &&sgn((l1.s-l2.e)^(l2.s-l2.e))*sgn((l1.e-l2.e)^(l2.s-l2.e))<=0;}bool interL2S(Line l1, Seg l2) {//判断直线和线段是否相交return sgn((l2.s-l1.e)^(l1.s-l1.e))*sgn((l2.e-l1.e)^(l1.s-l1.e))<=0;}Point getinterL2L(Line l1, Line l2) {//求直线和直线交点Point ret=l1.s;double t=((l1.s-l2.s)^(l2.s-l2.e))/((l1.s-l1.e)^(l2.s-l2.e));ret.x+=(l1.e.x-l1.s.x)*t;ret.y+=(l1.e.y-l1.s.y)*t;return ret;}Point getP2L(Point P, Line L) {//求点到直线垂足Point ret=L.s;double t=((P-L.s)*(L.e-L.s))/((L.e-L.s)*(L.e-L.s));ret.x+=(L.e.x-L.s.x)*t;ret.y+=(L.e.y-L.s.y)*t;return ret;}double disP2P(Point a, Point b) {//求点到点的距离return sqrt((a-b)*(a-b));}Point getP2S(Point P, Seg L) {//求点到线段最近的点Point ret=L.s;double t=((P-L.s)*(L.e-L.s))/((L.e-L.s)*(L.e-L.s));if(t>=0 && t<=1) {//返回垂足ret.x+=(L.e.x-L.s.x)*t;ret.y+=(L.e.y-L.s.y)*t;}else {//返回最近的端点if(disP2P(P, L.s) < disP2P(P, L.e)) ret=L.s;else ret=L.e;}return ret;}double disP2L(Point a, Line l) {return disP2P(a, getP2L(a, l));}double disP2S(Point a, Seg l) {return disP2P(a, getP2S(a, l));}Point getMirrorP2L(Point P, Line L) {//求点到直线的对称点Point ret=getP2L(P, L);return ret+ret-P;}Point Projection(Point p, Line l) {Point pr=((l.e-l.s)*((l.e-l.s)*(p-l.s)))/(l.e-l.s).len2();return l.s+pr;}pair<Point, Point> getinterL2C(Point o, double r, Line l) {Point pr=Projection(o, l), A, B;double d=disP2L(o, l);d=sqrt(r*r-d*d);if(sgn(d)==0) A=pr, B=pr;else {A=pr+(l.e-l.s).change_len(d);B=pr-(l.e-l.s).change_len(d);}return mp(A, B);}int main(){int T;Point A, B, V, O, P, Q, C;scanf("%d", &T);for(int tt=1; tt<=T; tt++){printf("Case #%d: ", tt);double rad;O.sc(), scanf("%lf", &rad);A.sc(), V.sc(), B.sc();P=A+V;if(pointCollinear(O, Line(A, P)))//与圆心共线{if(pointCollinear(B, Line(A, P)) && dirV2V(Vec(B-O), Vec(A-O))) printf("Yes\n");//B在AP线上且A、B在O同侧else printf("No\n");}else{if(sgn(disP2L(O, Line(A, P))-rad)>=0)//相切或相离{if(pointCollinear(B, Line(A, P)) && dirV2V(Vec(B-A), Vec(P-A))) printf("Yes\n");//B在AP线上且B、P在A同侧else printf("No\n");}else{pair<Point, Point> pp=getinterL2C(O, rad, Line(A, P));if(sgn(disP2P(pp.first, A)-disP2P(pp.second, A))<=0) Q=pp.first;//直线AP与圆离A更近的交点else Q=pp.second;C=getMirrorP2L(A, Line(O, Q));//A关于的对称点if(pointCollinear(B, Line(Q, A)) && pointOnSeg(B, Seg(A, Q))) printf("Yes\n");//B在QA线上且B在A、Q之间else if(pointCollinear(B, Line(Q, C)) && dirV2V(Vec(B-A), Vec(C-Q))) printf("Yes\n");//B在QC线上且B、C在Q同侧else printf("No\n");}}}return 0;}
day4 07.03
Contest:The 2015 ACM-ICPC Hefei Regional Contest
[ ] A Bus Routes
[ ] B Advanced Calculator
[ ] C Autopilot System
[ ] D Immortality of Frog
[🎈] E Land of Farms
[ ] F Matching Compressed String
[ ] G Alice’s Classified Message
[ ] H Frog and String
[ ] I The Shields
[ ] J Kingdom of Tree
Mumbles
打比赛心灰意冷系列,我tm脑壳坏了才去写B。先把昨天的计算几何题过了再考虑接下来的计划吧。
Solutions
【E】将数字相同的格子缩点,相邻格子连边,建图后求最大独立集,也就是求补图的最大团。这里注意一些概念:最小支配集(选择最少的点去覆盖所有点,一个点被覆盖后其相邻点也被覆盖),最小点覆盖(选择最少的点去覆盖所有边,一个点被覆盖后其相邻边也被覆盖),最大独立集(选择最多的点,使得它们互相不连通),最大团(选择最多的点,使得所有点之间都有边相连)。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100using namespace std;const int maxn=100+10;int dd[4][2]={0, 1, 0, -1, 1, 0, -1, 0};int T, n, m, N, a[maxn][maxn], id[maxn], ch[maxn][maxn];int ans, cnt[maxn], group[maxn], vis[maxn];char s[maxn];void Read(){N=0;memset(a, 0, sizeof a);memset(id, 0, sizeof id);for(int i=1; i<=n; i++){scanf("%s", s+1);for(int j=1; j<=m; j++){if(s[j]=='.') ch[i][j]=++N;else{if(!id[s[j]-'0']) id[s[j]-'0']=++N;ch[i][j]=id[s[j]-'0'];}}}for(int i=1; i<=n; i++)for(int j=1; j<=m; j++)for(int k=0; k<4; k++){int x=i+dd[k][0], y=j+dd[k][1];if(x>=1 && x<=n && y>=1 && y<=m){a[ch[i][j]][ch[x][y]]=1;a[ch[x][y]][ch[i][j]]=1;}}for(int i=1; i<=N; i++)for(int j=1; j<=N; j++)a[i][j]^=1;return ;}bool Dfs(int u, int pos){for(int i=u+1, j; i<=N; i++){if(cnt[i]+pos<=ans) return 0;if(a[u][i]){for(j=0; j<pos; j++) if(!a[i][vis[j]]) break;if(j==pos){vis[pos]=i;if(Dfs(i, pos+1)) return 1;}}}if(pos>ans){for(int i=0; i<pos; i++) group[i]=vis[i];ans=pos;return 1;}return 0;}int Work(){ans=-1;for(int i=N; i>0; i--){vis[0]=i;Dfs(i, 1);cnt[i]=ans;}return ans==-1 ? 0 : ans;}int main(){scanf("%d", &T);for(int tt=1; tt<=T; tt++){scanf("%d%d", &n, &m);Read();printf("Case #%d: %d\n", tt, Work());}return 0;}