====== 2019 Multi-University Training Contest 1 ====== ===== 比赛情况 ===== ^题号 ^A ^B ^C ^D ^E ^F ^G ^H ^I ^J ^K ^L ^M | ^状态 |Ø |O |- |O |O |Ø |- |- |Ø |- |- |Ø |Ø | //O 在比赛中通过 Ø 赛后通过 ! 尝试了但是失败了 - 没有尝试// **比赛时间** 2020-05-13 13:00-18:00 **提交记录** ''E: Wrong Answer 2020-05-13 13:23:37'' ''E: Wrong Answer 2020-05-13 13:28:32'' ''E: Accepted 2020-05-13 13:32:38'' ''D: Accepted 2020-05-13 13:38:53'' ''B: Wrong Answer 2020-05-13 13:55:48'' ''B: Accepted 2020-05-13 14:00:20'' ===== 题解 ===== ==== A-Blank ==== 补题 by Zars19 code:[[HDU6578]] 一个长度为$N$的序列被数字$\{0,1,2,3\}$填充,有$M$个限制条件,限制区间$[l_i,r_i]$中有$x_i$种不同数字。求方案数 题解:DP,用$f[i][j][k][l](i\geq j\geq k\geq l)$四维分别记录数字最后一次出现的位置,obviously它可以向$f[i+1][j][k][l],f[i+1][i][k][l],f[i+1][i][j][l]$和$f[i+1][i][j][k]$转移。而限制条件在循环到每一个$i$时对$r=i$的条件进行处理(将不满足者清零)。考虑到空间限制使用滚动数组。 ==== B-Operation ==== solved by infinity37 [[http://acm.hdu.edu.cn/showproblem.php?pid=6579]] === 题目大意 === 给你一个数列$a$,有两种操作 1:查询$a_l$到$a_r$间选若干数可计算出的最大异或和 2:在数列$a$后新增一个数 === 数据范围 === 数列长n,$1\leq n\leq 5e5$ 操作数m,$1\leq m\leq 5e5$ $1\leq a_i\leq 2^{30}$ 要求强制在线 === 题解 === ''警惕多组数据'' 求子集的最大异或和我们可以求助线性基,但是此题要求某个范围内的线性基,而且数列有可能修改。 首先对于区间问题我们可以转化为$(1,l-1)$和$(1,r)$两个区间,可以发现,前缀线性基是很好求的,因为对于到第$i$位的线性基和$i-1$位的线性基只差一个插入,所以我们就可以快速的求出前缀线性基。 但是如何把$(1,l-1)$的影响刨除呢?我们可以考虑在记录保留的数时,记录更偏右的数,然后在查询时查询只让位置在$l$之后的数产生影响即可。 官方题解是线段树维护线性基……等周末学习一个。 === 代码 === #include #include #include #include using namespace std; const int N = 1e6+5; const int M = 33; int pre[N][M]; int sel[N][M]; int n; int lastans = 0; void append(int x) { x = x^lastans; n++; int cur = n; for (int i = 31;i>=0;i--) { pre[n][i] = pre[n-1][i]; sel[n][i] = sel[n-1][i]; } for (int i = 31;i>=0;i--) { if ((x >> i) & 1) { if (!pre[n][i]) { pre[n][i] = x; sel[n][i] = cur; break; } else { if (cur > sel[n][i]) { swap(pre[n][i], x); swap(sel[n][i], cur); } x ^= pre[n][i]; } } } } void getans(int l,int r) { l = (l^lastans)%n+1; r = (r^lastans)%n+1; if (l>r) swap(l,r); int ans = 0; for(int i = 31;i>=0;i--) { if (sel[r][i] < l) continue; ans = max(ans,ans^pre[r][i]); } printf("%d\n",ans); lastans = ans; } int main() { int cas,pn,m,opt,x,y; scanf("%d",&cas); while(cas--) { n = 0; lastans = 0; scanf("%d%d",&pn,&m); for (int i = 1;i<= pn;i++) { scanf("%d",&x); append(x); } //lastans = 0; for (int i = 1;i<= m;i++) { scanf("%d%d",&opt,&x); if (opt == 0) { scanf("%d",&y); getans(x,y); } else { append(x); } } } } ==== D-Vacation ==== solved by Zars19 [[http://acm.hdu.edu.cn/showproblem.php?pid=6581|D-Vacation]] code:[[HDU6581]] 给出当前车辆、以及位置在当前车辆之前的所有$n$辆车的长度、前端距停车线距离、最大速度$l_i,s_i,v_i$,所有车都不可以超车,问当前车辆最快多久到达停车线。 题解:后来听说好多人是$O(n\log n)$做的,不过其实没有必要,可以做到$O(n)$。假定最终形态是当前车辆刚好到达停车线而它前面的车辆一字排开,计算时间然后取最大值即可。 ==== E-Path ==== solved by _wzx27 [[http://acm.hdu.edu.cn/showproblem.php?pid=6582]] 给一个图问割掉总权值最小的某些边使得最短路变长,先$\text{dijkstra}$跑一遍最短路,枚举边集把$w+dis[u]==dis[v]$的边加入网络流图中,跑一遍最大流即可。 $\text{HDU 5889}$几乎一样的题,看到以为能切,没想到因为$long\; long$的问题$\text{WA}$了两发。 #include #define ll long long #define int long long #define pii_ pair #define mp_ make_pair #define pb push_back #define fi first #define se second #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define show1(a) cout<<#a<<" = "<= i) { f[i] = min(f[i],f[j] + q); next = sam.get_next(next,s[i],mlen); } else { while (sam.getlen(next,s[i],mlen) + j < i) { sam.insert(s[j+1]); j = j+1; } next = sam.get_next(next,s[i],mlen); if (j < i) f[i] = min(f[i],f[j] + q); } } printf("%lld\n",f[len]); } return 0; } ==== I-String ==== 补题 by _wzx27 [[http://acm.hdu.edu.cn/showproblem.php?pid=6586]] 为什么当时没想出来这个贪心的策略啊。。。贪心的选$k$位中的每一位,选之前判断一下后面是不是可能满足条件 #include #define ll long long #define pii_ pair #define mp_ make_pair #define pb push_back #define fi first #define se second #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define show1(a) cout<<#a<<" = "< ==== L-Sequence ==== 补题 by _wzx27 把数列 $a_i$ 写成其生成函数的形式 $f(x)=\sum a_ix^i$,每个操作 $k$ 相当于 $f(x)\cdot \sum x^{ik}$,由交换律知顺序不重要,所以可以统计每种操作的次数 $m_i$,最后有 $$f(x)\cdot (\sum x^{i})^{m_1} \cdot (\sum x^{2i})^{m_2} \cdot (\sum x^{3i})^{m_3}$$ 其中$(\sum x^{ki})^m = \sum {i+m-1 \choose m-1}x^{ik}$ 另外模数刚好是998244353,做三次NTT即可 #include #define ll long long #define pii_ pair #define mp_ make_pair #define pb push_back #define fi first #define se second #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define per(i,a,b) for(int i=(a);i>=(b);i--) #define show1(a) cout<<#a<<" = "< \\ 给出一个集合,集合中元素是$(\boldsymbol{x_i},y_i)$,其中$\boldsymbol{x_i}$是一个$d$维向量,本题中$d=2$。$f(\boldsymbol{x})=sign(\sum^d_{i=1}w_i⋅x_i+b)=sign(\boldsymbol{w^T}⋅\boldsymbol{x}+b)$。问有没有一个向量$\boldsymbol{w}$可以使得所有$f(\boldsymbol{x_i})=y_i$。 题解:这题读题好难。。其实是计算几何,判断凸包是否相交。$x_1w_1+x_2w_2+b=0$是二维平面上的一条直线,一侧使得$f(\boldsymbol{x_i})$为$1$,另一侧为$-1$。问题转化成有没有一条直线可以把两个点集分开,于是又等价于对两个点集求凸包判断是否相交。判断凸包相交可以$O(n^2)$做,先判两凸包上的边两两是否线段相交,再看一凸包中的每一个顶点是否在另一凸包内。 ===== replay ===== ==== 13:00-13:30==== 开场 _wzx27开E,infinity37从前往后,Zars19从后往前 _wzx27提出最小割做法,三人讨论,确认可行,开始写题 infinity37提出B像可持久化trie,_wzx27提出像线性基,infinity37和Zars19思考过后认为确实更像线性基 Zars19提出D题做法 _wzx27提交E,错误,Zars19开始写D 经过两次修改,E正确 ==== 13:30-14:00 ==== infinity37对B提出线性基前缀做法 Zars19提交D,正确 infinity37开始写B _wzx27和Zars19f开I,L,认为L可贪心 infinity37提交B,错误 ==== 14:00-14:30 ==== 认识到没有注意多组数据的清空,infinity37提交B正确 三人陆续读题,未果 ==== 14:30-15:30 ==== 一个小时内没有新的思路,于是结束了比赛,开始补题 ===== 总结 ===== ==== infinity37 ==== 本场比赛数学题居多,而队内三人都对数学题不太了解,因而做不出题目。 另外,本场比赛中我发现了自己对后缀自动机的了解完全不够,只能写板子套题,而对后缀自动机的原理了解不够深刻,所以尽管看出F需要用后缀自动机解但是没能想明白应该如何解。因此我还需要对后缀自动机进行复习。 ==== _wzx27 ==== 太菜了,要恶补数学(字符串的题也完全不会)。而且真的想5个小时感觉体力也不太够(不过男人不能说不行 ==== Zars19 ==== 好…好难。只写出了一眼就想到的题。还需要多多做题,多多学习知识点。没有发现计算几何的题是道计算几何,在题目太长难读的时候要有抗压能力,要锻炼建模转换的思想,要有不屈的精神…