停课总结
因博主太菜改不完题目,故停更。
11.1 总结
上午
\(T0\) 记搜走迷宫果然布星。
\(T1\) 线性基就放弃吧。
\(T2\) 类似正方形计数的方法,推出一个矩形里三角形的个数然后枚举矩形大小就行了。
\(T3\) 优化建图+拓扑排序最长路
晚上
\(T1\) 贪心模拟题
\(T2\) 点我
\(T3\) 有些细节的分层图最短路。
10.31 总结
\(T1\) 感觉和餐巾计划问题有点相似,但又有些不同。。表示低智商选手不适合做这种题。
枚举+贪心+二分。
\(minv[i]\)表示保质期大于等于\(i\)的最小的价格,可以线性倒推出来。
\(f[i]\)表示只买一次外卖,存活\(i\)天最少费用,\(f[i]=f[i-1]+minv[i]\)
然后枚举总共买外卖的次数\(k\),从\(1\)到\(\lfloor M/F \rfloor\),二分找出满足\(k*F+k*f[x]<=M\)的最大的\(x\),那么答案就是\(k*x+\lfloor (M-k*F-k*f[x])/minv[x+1]\rfloor\)
\(T2\) 这个出题人怎么这么坏?????迷惑性好强。读入的都是整数,答案哪来的小数?而且举例还举\(t=6.5\)的例子,答案还要求保留\(6\)位小数,真的**。考试时一直没想明白什么时候会出现小数,而且大样例答案也是个整数。而且很容易证明最优解一定出现在某个峰值。。但我不敢写。还是写了个膜你退火,但没考虑负数,所以只有\(35'\),真的被这题恶心死了。直接枚举所有峰值跑最小生成树就好了。
\(T3\) 类似分层图最短路,加一个时间维度跑\(Dijkstra\)就行了。
10.30 总结
10.29 总结
晚
\(T1\) 完全没思路,,设\(x=ap+b\)后代入可以消掉二次以上的项,然后扩欧搞就行了。
\(T2\) \(map\)水了\(70'\),应该用字符串哈希判重
\(T3\)
\(T4\) 树形\(DP\),\(f[i][j]\)表示以\(i\)为根的子树保留\(j\)个结点最少需要砍掉的节点数,边界\(f[u][size[u]]=1,f[u][0]=0,f[root][size[root]]=0\),其它均为\(INF\),然后跑树形背包就行了,枚举从每个子树取多少。
雅礼\(NOIP\)模拟赛
Day1
很慌,感觉\(rp\)要掉光了。
\(T1\) 简单枚举
\(T2\) 一开始以为是很简单的一个\(DP\),令\(f[i][j][k]\)表示\(a\)串前\(i\)个、\(b\)串前\(j\)个、总共分成了\(k\)个集合的最大长度。然后过了小样例,过不了大样例,才反应过来,不能直接转移,因为当\(a[i]=b[j]\)时,我们有两种选择,一是新建一个集合,二是放到上一个集合中,而后者有个前提条件,就是\(a[i-1]=b[j-1]\)且其更新了\(f[i-1][j-1][]\)的答案,这个不好判断,所以我干脆换个思路,\(f[i][j][k]\)表示......且\(a[j],b[j]\)必选的情况的最长长度,显然当\(a[i]!=b[j]\)时\(f[i][j]\)都为\(0\),当等于时,一是放到上一个集合\(f[i][j][k]=f[i-1][j-1][k]\),而是新建一个集合\(f[i][j][k]=max_{p=1}^{i-1}\max_{q=1}^{j-1}f[p][q][k-1]\),后面那部分维护一个前缀最大值就行了。
\(T3\) 莫队水了\(30'\)
一中\(NOIP\)模拟赛
Day1
\(T1\) 递推套一个矩阵加速就行
\(T2\) 。。大中模拟题,我果然写瓦了,只有\(70'\)
\(T3\) 某谷有原题,是黑题,。。暴力\(25'\)
\(100+70+25=195\)
Day2
\(T1\) 组合数模板题,开始还以为是卢卡斯定理,但是发现\(n\)很小,模数很大,于是直接公式跑,求个逆元就行
\(T2\) 发现\(m1=m2=1\)的部分分有\(75'\),果断放弃正解,于是变成了裸的最长不下降子序列
\(T3\) \(string\)递归直接暴力搞,因为常数小,卡过了,正解应该要用链表。
\(100+75+100=275\)
\(195+275=470\)
10.27总结
\(T1\) 0
其实,,我先是写了个记忆化,写完过了样例就没管了,中间发现可以倍增,而且倍增很简单,但我懒没写。。然后就听取\(Wa\)声一片了。
其实是读入问题。。
\(T2\) 5
带权并查集实现出了点问题。。
\(T3\) 45
???
三分水了\(45'\)
\(T4\) 100
我写了个维护最大值的线段树,其实可以直接优先队列跑的。。
但考试标题是线段树啊。。
10.25总结
上午
\(T1\)
最短路板子题然而我并没有看出来
\(T2\)
同 10.23 \(T3\)
\(T3\)
斜率优化\(DP\),还没有改。
\(T4\)
用并查集把所有有共同的\(x\)或\(y\)坐标的点连起来,然后答案就是每个连通块里不同\(x\)的个数乘以不同\(y\)的个数的和。
晚上
\(T1\)
基环外向树找环,其实可以拓扑排序一下剩下的就是环了,但我用的\(Tarjan\)
\(T2\)
大家都是找桥然后判是否在路径上,但我用的找必经之点的方法:圆方树。对图建出圆方树后,1~n的路径中经过的原来存在的边之间的原点的数量即为所求,
比如说 方1-圆-方2,图中本来就有方1-方2的边,如果树上1~n的路径经过这个原点,那么原来这条边就是必经边。
但是数据里有重边,然后我就\(Wa\)了几个点,因为是\(Subtask\)机制,所以完美爆\(0\)。
\(T3\)
10.23 总结
\(T1\)
简单膜你,不知道为什么Wa了几个点。
\(T2\)
按题意膜你建图跑最短路救星。
\(T3\)
让所有的点之间均能通信,为一个生成树结构。题目就是求出这N个点的最小生成树。然后将最小生成树分成S个割集,割集之间采用卫星通信,割集之内采用无线通信,求出割集之内点之间的最大值即可。
可以证明,割集之内的点距离的最大值的最小值为最小生成树的N-1条边从大到小排列后第S个边长。采用kruskal算法解决。
\(T4\)
二分图最小顶点覆盖=二分图最大匹配,所以直接跑匈牙利救星。
10.22 总结
上午考试
\(T1\) west
把每个基地一起\(push\)到堆里跑\(Dijkstra\)就行。
\(T2\) east
我写的二分答案然后跑一遍\(SPFA\)判定,又\(WA\)又\(T\)。
其实只要建反图,然后跑一遍\(SPFA\)最短路就行了,起点是\(n\),答案就是\(dis[1]\)。
需要注意的是当跑到某点发现\(dis\)小于\(1\),就要把\(dis\)改为\(1\),因为我们要保证任意时刻都是有农民军的。
\(T3\) party
这不就是《没有上司的舞会》吗
\(T4\) mst
最小生成树模板题。
晚上考试
\(T1\) 联系圈
强联通分量模板题
\(T2\) 间谍网络
因为有两处细节错误,所以只有\(49'\)。
先\(Tarjan\)缩点,然后找出所有入度为\(0\)的点,如果有任何一个入度为\(0\)的点不能被收买那么显然无解, 否则一定有解,答案就是入度为\(0\)的权值和。
对所有入度为\(0\)的点跑一遍\(DFS\)标记所有能到的点。最后遍历一遍看哪个点没被标记输出\(NO\)就行了。
\(T3\) 公路修建
完全图最小生成树,要用\(Prim\)然而我不会,只好用Kruskal水了50分
同时注意常数优化
\(T4\) 访问“美术馆”
树形\(DP\),递归读入建树。
设\(f[u][i]\)表示在以\(u\)为根的子树偷\(i\)幅画用的最少时间,初始化\(f\)全为\(INF\),
然后跑树形背包就行了。
10.21 总结
今天考的洛谷月赛。然而我并不知道(雾
\(T1\) 浏览器
我想歪了
我觉得因为\(1^1=0\),所以异或后的数的\(1\)的奇偶性和原来两个数\(1\)的奇偶性无关。
事实上是有的,因为\(1^1=0\),这里的\(1\)是成对变成\(0\)的,这并不影响整体的奇偶性。
所以,当两个数\(1\)的个数一奇一偶时,异或后\(1\)的个数为奇。
于是只需要统计奇偶个数,然后乘法原理一下就好了。
直接统计时间复杂度是\(O(n\log v)\),虽然理论上不能过\(10^7\)的点,但实际上随机数据的\(1\)的个数远远跑不满整个\(log\),是可以水过的。
当然不能局限于\(A\)掉这道题。事实上统计\(1\)的个数是有\(O(1)\)的做法的。
那就是预处理\(2^16\)以内的数,然后把要处理的数分为前\(16\)位和后\(16\)位分开统计就行了。
\(T2\) 大师
容易看出是\(DP\),但我状态想错了。最后干脆就没写了。
枚举公差\(d\),然后设\(f[j]\)表示以第\(j\)个数结尾,公差为\(d\)的个数。我们没必要去前面找刚好比\(a_j\)小\(d\)的数,\(v<=20000\)就告诉我们可以用桶装。
于是设\(g[k]\)表示以\(k\)值结尾的公差为\(d\)的等差数列个数,然后就有
\(T3\) 礼物
10.20 总结
上午\(YLOJ\)的模拟考试
\(T1\) 空间复杂度
和\(NOIP2017D1T2\)极为相似,考察的是字符串处理和模拟。
\(T2\) 统计正方形
容易发现每个正方形占用的格子一定都是\(m\times m\)的,而且\(2\times 2\)的格子里有两个正方形,\(3\times 3\)的有3个……
于是,只需统计有多少个\(m\times m\)的子矩阵就行了。
for(int i = 1; i <= min(n, m); ++i)
ans += i * (n - i + 1) * (m - i + 1);
我没取模,只有\(80'\)...
\(T3\) 最长不下降子序列
根据数据范围,我们需要设计一个\(O(n)\)或者\(O(n\log n)\)的算法。
我们的最长不下降子序列肯定是由若干的\(1\)和若干个\(2\)依次组成。
若不考虑旋转,我们可以直接统计\(1\)的个数前缀和和\(2\)的个数后缀和,然后枚举分界点,\(O(n)\)得出答案。
现在我们来考虑旋转带来的影响。
可以发现,旋转可以让一些\(1\)到前面来,让最长不下降组序列的\(1\)的部分增加一些数,但同时也会使一部分\(2\)旋转到\(1\)的前面,导致无法统计到最长不下降子序列里。
于是设\(1\)为\(1\),\(2\)为\(-1\),然后旋转包含\(i\)在内的区间的贡献就是以\(i\)为结尾的最大字段和。
所以我们\(O(n)\)预处理最大子段和和后缀最大值,然后乱搞就好了。
我因为把\(100W\)写成\(10W\)数组开小所以\(RE\)了几个点。但我的方法是对的。
能在考试的时候想到大家想不到的方法,心里还是那个啥
\(AC Code:\)
const int MAXN = 1000010;
int l[MAXN], r[MAXN], p[MAXN], a[MAXN], b[MAXN], s[MAXN], m[MAXN], n, ans, tot[3];
int main(){
n = read();
for(int i = 1; i <= n; ++i)
++tot[a[i] = read()];
for(int i = 1; i <= n; ++i)
l[i] = l[i - 1] + a[i] % 2, r[i] = tot[2], tot[2] -= a[i] - 1, b[i] = a[i] == 1 ? 1 : -1;
for(int i = n; i; --i){
if(s[i + 1] > 0)
s[i] = s[i + 1] + b[i];
else s[i] = b[i];
m[i] = max(m[i + 1], s[i]);
}
for(int i = 1; i <= n; ++i)
if(a[i] == 2)
ans = max(ans, l[i] + r[i] + m[i + 1]);
if(!ans) ans = n;
printf("%d\n", ans);
return 0;
}
晚上的考试
\(T1\) 排序
按题意正向连边
容易发现只有几个点在同一条链上,他们两两不能在同一集合内。
于是题目就变成了求有向有环图的最长链。\(Tarjan\)缩点+拓扑排序最长路就行了。
\(T2\) 排列
。。。打表找规律吧(虽然我有意识但没找出来,orz zyf找规律大佬
f[i][j] = f[i - 1][j] * (j + 1) + f[i - 1][j - 1] * (i - j);
\(T3\) 速度限制
注意到\(V<=500\),类似于分层图最短路,对每个点添加一维速度,然后跑最短路。
\(T4\) 服务器信息存储问题
怎么说呢,心理阴影,此题不可做
\(T5\) 棋盘染色
第一眼就想到了直接贪心,脑补一下就好,至少我不能证伪
答案就是每一行、每一列\(1\)的个数的最大值
(某童鞋由于多次CopyTJ,故不打码)
10.19 总结
\(T1\) 特工
点我
\(T2\) 洞穴
不知道为什么错了,我是新建了一个\(1\)的副本,把所有终点为\(1\)的边的终点改为这个副本,最后跑从\(1\)到副本的最短路,不知道为什么只有\(62'\)。
野佬的方法:对\(1\)连接的所有点跑一遍到\(1\)的最短路(删去这个点与\(1\)的直接连边),取最小值,加剪枝后不会超时。
\(T3\) 最短路
板子题,记得开\(long long\)
\(T4\) 最小差异值
我记得这题是考过的,当时大家都没有想到。
当最小边确定后肯定是要尽量选边权接近它的边才能使差值最小。
于是排序后枚举最小边,跑\(m-n+1\)遍\(Kruskal\)就行了。
10.18总结
\(T1\) 最优贸易
点我
\(T2\) 靶形数独
启发式搜索,每次从可能性最少的格子开始填。因为没时间了我就没写。
当然,还有更优秀的算法:舞蹈链(\(DLX\)但我不会啊)
\(T3\) 关押罪犯
点我
\(T4\) 引水入城
可得从一个点发出的水流到最后一行一定是连续的一段区间,否则中间不连续部分则被封锁,无解。
搜出第一行每个点能流的区间,然后就是跑区间覆盖问题了,贪心或者\(DP\)都行。
10.16总结
关于做了什么题从此开始不在这个总结里写了,可以看我的博客里非总结的文章,或者直接去洛谷看评测记录。
晚上的考试。
\(T1\) 硬木木板
数据好像有问题
搜索应该是20分
正解状压\(DP\),设\(f[i][S]\)表示前\(i-1\)行都填满了、第\(i\)行状态为\(S\)的方案数。
然后因为有\(L\)型的存在,直接线性推不好推,所以用\(DFS\)转移状态。
\(T2\) 通向自由的钥匙
裸的树形背包,但我因为把一个\(>=\)写成了\(>\),然后就爆\(0\)了。
\(T3\) 三角蛋糕
\(N<=100\),显然可以用\(N^3\)的暴力。
于是预处理三角形前缀和,枚举顶点,一行一行扩展,更新答案即可。
注意一下要结合题目,就是说每个小三角形有方向,不是随便几个都能构成三角形,要判断奇偶性。
\(T4\) 骑士
我因为没特判\(n=1\)的情况\(WA\)了两个点。。
设\(f[i][S_1][S_2][j]\)表示前\(i\)行,第\(i\)行状态为\(S_1\),第\(i-1\)行状态是\(S_2\),总共放了\(j\)个骑士的方案数。
然后两个循环预处理\(f[2]\),从第三行开始,
枚举行i
枚举本行状态S1
枚举上一行状态S2
枚举上上行状态S3
如果这三行互不矛盾
枚举j(从getnum(S1)+getnum(S2)+getnum(S3)到骑士总数)
f[i][S1][S2][j]+=f[i-1][S2][S3][j-getnum(S1)]
其中,\(getnum(x)\)表示\(x\)二进制表示下\(1\)的个数。
\(getnum\)的实现(树状数组的思想)
inline int getnum(int x){
int ans = 0;
while(x){
++ans;
x ^= (x & (-x));
}
return ans;
}
\(x\&(-x)\)即\(lowbit\)运算。
时空都卡的比较紧,别开太大了,我就是卡着开的,同时注意常数不要太大。
为什么不能像互不侵犯那题一样只记录当前行的状态?
因为当前行合不合法取决于前两行,如果只记录本行的状态怎么转移?从上一行?从上上行?还是加起来?
无论如何,都会有重复或漏掉。
(\(Rank1\)由于\(Copy\&Paste\)实锤,....)
10.15 总结
复习了矩乘,尝试去写\(DLX\),没写出来,算了。
晚上的考试
\(T0\) \(Hankson\)的趣味题
我直接枚举\(b1\)的所有约数,判断是否符合要求。
然后就水过了。
\(T1\) 青蛙的烦恼
我写了个假的贪心,没骗到分。。。
易得最短路径里肯定没有交叉的路线。
所以设\(f[i][j]\)表示从\(i\)到\(j\)经过沿途所有数的最短路,则\(f[i][j]=min(f[i+1][j]+dis(i,i+1),f[j][i+1]+dis(j,i))\)
\(T2\) 警卫安排
题目里直接给的每个点的子节点,然后我就没建无向边了,建的有向边,而且直接从\(1\)开始。。。然而\(1\)并不是根。因此爆\(0\)了。
加一个循环找根就\(A\)了。
\(f[u][0]\)表示不选\(u\),但是子树里至少选一个的最少花费,前提子树都满足要求
\(f[u][1]\)表示选\(u\)的最少花费,前提子树都满足要求
\(f[u][2]\)表示不选\(u\)而且子树都不选的最少花费,前提除\(u\)外子树都满足要求
那么有
\(f[u][1]=\sum min(f[v][0],f[v][1],f[v][2])\)
\(f[u][0]=\sum min(f[v][0],f[v][1])\)(若子树全不选则强制选一个\(f[v][0]\)和\(f[v][1]\)相差最小的。)
\(f[u][2]=\sum f[v][0]\)
最终答案是\(min(f[root][0],f[root][1])\)
\(T3\) 最短回文串
我又写了个假的\(DP\)
把原串反过来,跑一边和原串的\(LCS\),用长度减掉\(LCS\)即为答案。(不要问我怎么证)
\(T4\) 平板涂色
第二次做这题。记得第一次\(ych\)大佬搜索拿了\(72'\)暴虐我们。
现在的水平肯定是比当年高了不少的,\(n<16\),我肯定是看出了这题是状压\(DP\)的,但是不太好实现,所以我就写了个搜索又写挂了mmp
我把排序和预处理写反了,居然还有\(72'\)?,改过来就\(A\)了。
就是预处理每个矩形的前提矩形,然后枚举每次涂什么颜色,加了点剪枝跑的飞快。
状压\(DP\):
\(f[S][u]\)表示状态为\(S\)的情况下最后一个取的是\(u\)的最少涂色次数,然后每次找一个不在集合\(S\)里的矩形涂色,如果颜色和\(u\)不同,则\(+1\),否则不\(+\)。
题外话:对比其他人的总结,我是不是写的太详细了点?
应老师要求,放出成绩
10.14总结
\(T1\) 火车进站
只会做\(m=1\)的情况。。拿了\(20‘\)
对\(m\)分三种情况讨论
\(m=1\),按入站时间排序
\(f[i]\)表示前\(i\)个火车最多能进几个
\(f[i]=max_{\text{j<i且j出站时间<=i进站时间}}f[j]+1\)
\(m=2\),按入站时间排序
\(f[i][j]\)表示前\(j\)个火车\(i,j\)同时在站内最多能进站多少个。
\(f[j][k]=max_{i<j且i出站时间<=k入站时间}f[i][j]+1\)
\(m=3\),按出站时间排序
同理。
#include <cstdio>
#include <algorithm>
using namespace std;
#define Open(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define Close fclose(stdin);fclose(stdout);
const int MAXN = 110;
struct Train{
int in, out;
bool operator < (const Train A) const{
return out < A.out || out == A.out && in < A.in;
}
}e[MAXN];
int cmp(const Train a, const Train b){
return a.in < b.in;
}
int f[MAXN], ff[MAXN][MAXN], F[MAXN][MAXN][MAXN];
int n, m, a, b, ans;
int main(){
Open("train");
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
scanf("%d%d", &e[i].in, &e[i].out);
if(m == 1){
sort(e + 1, e + n + 1, cmp);
for(int i = 1; i <= n; ++i){
f[i] = 1;
for(int j = 1; j < i; ++j)
if(e[j].out <= e[i].in)
f[i] = max(f[i], f[j] + 1);
ans = max(ans, f[i]);
}
}
if(m == 2){
sort(e + 1, e + n + 1, cmp);
for(int i = 1; i < n; ++i)
for(int j = i + 1; j <= n; ++j){
if(e[i].out > e[j].out) continue;
ff[i][j] = 2;
for(int k = 1; k < i; ++k)
if(e[k].out <= e[j].in)
ff[i][j] = max(ff[i][j], ff[k][i] + 1);
ans = max(ans, ff[i][j]);
}
}
if(m == 3){
sort(e + 1, e + n + 1);
for(int i = 1; i <= n; ++i) F[0][0][i] = 1;
for(int i = 0; i < n; ++i)
for(int j = i + 1; j <= n; ++j)
if(e[i].in <= e[j].in)
for(int k = j + 1; k <= n; ++k)
if(e[j].in <= e[k].in){
for(int l = 0; l < max(i, 1); ++l)
if(e[l].out <= e[k].in)
F[i][j][k] = max(F[i][j][k], F[l][i][j] + 1);
ans = max(ans, F[i][j][k]);
}
}
printf("%d\n", ans);
return 0;
}
\(T2\) 凸多边形的三角剖分
当时是没想明白。。
\(f[l][r]\)表示\([l,r]\)的子凸多边形的最小乘积之和。
\(f[i][j]=min(f[i][j],f[i][k]+f[k][j]+w(i,j,k))\)
\(T3\) 单词前缀
数组开小只有\(20'\)qwq
很简单的一个\(DP\),\(f[i]\)表示前\(i\)个的前缀能否拼出
然后对于每个位置\(i\),枚举每个单词\(a_j\),若\(f[i-len_j]=1\)且后缀和这个单词能匹配,那么\(f[i]=1\)。
加些剪枝就行了。
\(T4\)
\(T5\) 选课
树形\(DP\)就不用说了。
主要难在输出方案。
很容易想到一个方法就是开一个三位数组记录每个状态的方案,然后更新状态的时候合并方案。
但\(500^3\)的空间显然不可忍受,vector的遍历我不会啊
所以我想到一个新方法,就是只记录每个状态是由哪两个状态更新而来的,最后递归处理就行了。
但瓦掉了。
为什么?
因为假设\(f[1][10]\)是由\(f[1][5]\)和\(f[2][5]\)更新而来的,而\(f[1][5]\)是由\(f[1][1]\)和\(f[2][4]\)更新而来的,这样就会造成重复选同一个子树,然后就漏掉了一些路径。
对此,我的解决方案是先把两个状态里不是本结点的那个标记一下,然后重新更新本结点的那个状态,跳过打了标记的。然后就\(A\)了。
10.11 总结
\(T1\) 三角形牧场
正解是\(DP\),但我用rp优化的搜索水过了。点我
正解:
\(f[a][b]\)表示三角形两条边长度分别为\(a,b\)的情况是否存在,然后跑可行性转移就好了。
M_sea是巨佬,随便过了。
\(T2\) 多米诺骨牌
考试时我写了个有后效性的\(DP\),还写错了,只拿\(32'\),改对那个有后效性的\(DP\)后有\(72'\)。
正解:
状态:\(f[i][j]\)表示前\(i\)个第一行的和为\(j\)的最小翻转次数。
边界:\(f[1][b[1]]=1,f[1][a[1]]=0\),其它均为\(INF\)
转移:\(f[i][j]=min(f[i-1][j-a[i]],f[i-1][j-b[i]]+1)\)
\(T3\) 最长上升子序列
二分的方法我不记得了。
就写了个值域线段树代替。
然后有个地方\(num\)写成了\(n\),然后就只有\(40'\)了。
\(T4\) 修剪草坪
\(O(N^2)\)的\(DP\)也写挂了。
\(f[i]\)表示前\(i\)的最大效率。
则有\(f[i]=max(f[j-1]+sum(j+1,i))(i-k<=j<=i)\)
设\(s\)表示前缀和,
变形得\(f[i]=max(f[j-1]-s[j])+s[i](i-k<=j<=i)\)
\(max\)里那段就是滑动窗口了,用单调队列维护。
\(T5\) A simple Task
10.9 总结
上午考试下午改题晚上滚粗。
\(T1\) 小木棍
搜索+剪枝
枚举\(i\),当满足\(i|\text{总长度}\)时,搜索\(check\),若满足条件,直接输出。
\(check\)加些剪枝就好了。
\(T2\) 邮票面值设计
也不是第一次做了,搜索+\(DPcheck\)
\(T3\) 魔板
和《新汉诺塔》一样。是以前心中的“不可做题”。
不过现在总是搞懂了。
枚举每一列作为最终序列的第一列,和目标序列的第一列对比,如果不相同就把这一行翻转。
然后\(N^3\)匹配后面的列就行了。
int a[110][110], b[110][110], d[110], c[110], e[110], g[110], vis[110], n, m, f;
int judge(int x){
d[x] = 1;
for(int i = 2; i <= m; ++i){
int flag = 0;
for(int j = 1; j <= m; ++j){
if(d[j]) continue;
for(int k = 1; k <= n; ++k)
if((a[k][j] == b[k][i]) == c[k])
if(k == n){ flag = 1; d[j] = 1; }
else 233;
else break;
if(flag) break;
}
if(!flag) return 0;
}
return 1;
}
int main(){
Open("panel");
int k;
scanf("%d", &k);
for(int xjc = 1; xjc <= k; ++xjc){
memset(vis, 0, sizeof vis);
memset(e, 0, sizeof e);
memset(g, 0, sizeof g);
f = 0;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
scanf("%d", &a[i][j]), e[i] += a[i][j];
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
scanf("%d", &b[i][j]), g[i] += b[i][j];
for(int i = 1; i <= n; ++i)
if(e[i] != g[i] && m - e[i] != g[i]){
f = 1;
break;
}
if(f){ printf("NO\n"); continue; }
for(int i = 1; i <= m; ++i){
memset(d, 0, sizeof d);
for(int j = 1; j <= n; ++j)
c[j] = a[j][i] == b[j][1];
if(judge(i)){ f = 1; break; }
}
if(f) printf("YES\n");
else printf("NO\n");
}
\(T4\) 多元高次方程
采用“中途相遇”。
直接暴力枚举6个数时间要\(150^6\),显然会炸。
所以我们只要枚举前三个,用哈希记录每个数出现的次数,再枚举后三个数与之匹配就行了。
\(T5\)
听说正解代码\(600\)多行?
\(NOI\)选手都这么会码的吗。
\(100+90+0+100+20=310\) \(rk1\)
10.8 总结
写了T
今天的考试:
\(T1\) 覆盖问题
不知道为什么考试时就是没写,其实就是个很简单的搜索.
枚举每个点,如果放了就直接到下一个点,如果没放,就对那四种形状每个都试一次看能不能放.
一个剪枝:如果上上行有地方没放,直接返回,因为本行无论如何都是无法影响到上上行的.
\(T2\) 解集个数
搜索,枚举每一个数,从上一个数开始枚举,记录当前的和不是用\(double\),而是分别记录分子和分母,每次加上\(\frac{1}{k}\)都先通分再约分,这样可避免精度问题,也方便了后续的处理.
可能是我常数太大了,\(T\)了3个点.
\(T3\) 黑白棋游戏
应该是第三遍做了,但这次多了个输出方案,第一问见这里
我们已经把所有要进行的操作处理出来了.
然后怎么输出方案?我一开始就是简单的把要移的直接先\(x\)后\(y\),\(L\)字型移过去,但事实上不行.
因为在路上我们会改变其它棋子的状态.
我们把交换棋子抽象成移动黑棋,把白棋视为空气,把沿途的黑棋视为障碍.
当碰到一个障碍,就把这个障碍移到终点,再把原来这个棋移到障碍的位置上,递归处理.
\(T4\) 家族史图
可能是道大模拟题吧.题面是挺有意思的.网上也搜不到,讲题也没讲,就算了吧,没有意义.
\(T5\) 单词接龙
经典的搜索题(虽然状压\(dp\)也能做)
需要一个剪枝(剪枝方法有很多)
比如说:不用保存字符串,直接用结构体记录首字母、尾字母和长度,然后按长度排序。
相同开头结尾的只跑第一个,一定最优。这样就能过了。
(出题人肯定会想法设法卡你就像这样)
ABBA
AOOA
AXXA
AIIA
APPA
AUUA
\(20+72+70+0+72=234\) \(rk3\)
10.7总结
今天改了题,写了一遍双联通分量缩点。。
上午的考试
\(T1\) 金明的预算方案
有依赖的背包问题,我没考虑两个附件都选的情况,就只有\(40'\)
\(T2\) 传纸条
原题
\(T3\) 树网的核
找出一条直径,算出这上面所有点到直径之外点的最远距离,记为\(m\)
然后在这条直径上尺取就行,答案是\(max(m,\text{前端长,后端长})\)
\(序关系计数问题\)
是个\(DP\),有点像第二类斯特林数,没怎么懂
\(dp[i][j]=(j+1)*(dp[i-1][j-1]+dp[i-1][j])\)
\(dp[i][0]=1\)
\(ans=\sum_{i=1}^ndp[n][i]\)
\(商店购物\)
有点复杂
10.6总结
神奇。今天是我生日
我居然用了半个下午去写、一个晚上去\(debug\)一道毒瘤题。
提交记录点我
今天上午的考试
\(T1\) 能量项链
断环成链然后区间\(DP\)
\(T2\) 虫食算
不是第一次做了。搜索+剪枝。
\(T3\) 过河
\(30'\)的\(DP\)很好像。
考试的时候就想到肯定是可以离散化的,怎么离散化?
比如说两块石头之间的距离为\(10000\),但事实中间都是空的,这样直接跑一遍没有意义。
若\(s!=t\),则说明这个区间至少有两个数,而连续的两个数一定是互质的,互质我们能得到什么呢?
有没有想到\(NOIP2017D1T1\)小凯的疑惑,两个互质的数最大不能拼出的数是\(a*b-a-b\)。
所以直接用这个结论,当两点距离大于\(100\),直接把距离变成\(100\),因为第二块石头前面的\(t\)块石头都是可以到达的。
有个特例就是\(s=t\),这个时候我们要把距离变为\(s\)加上距离模\(s\)的值。
\(T4\) 篝火晚会
应该是结论题吧。构建出目标数列,然后和目标位置不同的数量即为答案。
因为是一个环,所以怎么求这个数量是个问题。暴力\(O(n^2)\)就不用说了,肯定是过不了的。
我们算出每个数从左和从右到目标位置的距离,可以发现这个距离相等的始终相等。
于是我们找出最多的距离相等的数,当这个距离变为\(0\)时,答案最优。
\(T5\) 等价表达式
码农题吧。
首先解析表达式,然后随机几个值带进去,看是否相等就行了。
10.5 总结
做了一道\(DP\)题,学会了树状数组区间修改区间查询,常数优秀。改了题(过于毒瘤的没改)
上午考试,
\(T1\) 神经网络
我可能写了个假的拓扑排序
只有\(42'\)
\(T2\) 侦探推理
ttm毒瘤了吧。
我码了好久一百多行,结果有个小\(bug\)还没人家输出一个"Impossible"分多。mmp
不想改了。
\(T3\) 加分二叉树
至少是第四次碰到了,不说了。
\(T4\) 传染病控制
正解是搜索,枚举每次切哪个。
我贪心骗了\(90'\)(What?)
又到中间去了。
10.4总结
今天学了\(manacher\)算法。
上午是考试,炸裂
\(T1\) 进制转换
完全没往正数进制转换上想,死找规律,没找出来,\(GG\),我可能是唯一一个没切掉此题的
和正数进制转换类似,短除法,处理一下负数取模就好了。
\(T2\) 方格取数
做了多少遍了
\(T3\) Car的旅行路线
一个很裸的最短路,就是建图有点复杂。
我求第四个点的时候求错了,所以只有\(60'\)
\(T4\) 矩形覆盖
看数据范围,\(n<=50,k<=4\),很可能是搜索,然而我不会。
就是枚举每个点放到哪个\(k\)里面,暴力搞搞就好了。
10.3总结 & 10.4计划
昨天做了区间,计算器,可爱的质数.
晚上是考试。
考试第一个小时都在看题和想做法,根本没有动手,因为在等数据范围,没数据范围到时候写了个算法发现数组都开不下就\(GG\)了。
\(J\text{哥}:\text{今天都是贪心题}\),\(GG\)预定。
\(T1\) inte
这不是尼克的任务吗,保留意见等数据。
发现的确可行,这\(100'\)到手。
\(T2\) job
一个很简单的贪心,不知道\(O(n^2)\)能不能过,保留意见。
当然是可以的啦,\(+100'\)
\(T3\) maxm
想了一下发现想不出,就放弃了。
原来是贪心拆\(5\),如果整除就全拆\(5\),否则对余数分情况讨论,膜你大法好。
\(T4\) robot
直接大暴力\(DP\),时间复杂度\(O(nmk)\),水过了。
\(T5\) task
被题目迷惑了,以为是\(T2\)的加强版,根本没想到背包\(qwq\)
其实就是个裸的\(01\)背包,改下转移的上下界就好了。
\(T6\) thuff
是\(Huffman\)的思想,第一眼看题还以为和《荷马史诗》差不多,结果发现鸟关系都没有。
最大值:每次合并两个最大的
最小值:每次合并\(k\)个最小的
实现嘛,我懒就用优先队列了。
因为没开\(long long\), \(WA\)了\(2\)个点。
\(99+96+0+99+11+81=387,rk3\)
10.4计划:学习马拉车,.......
9.29总结 & 9.30计划
昨天改了题,去写《HH的项链》,主席树做法已实现,但因为空间卡的太紧,只能拿\(80'\)。
晚上是考试..
\(T1\) 餐巾计划问题
考试写这题还是算了吧。
这题用网络流或者贪心+三分都能做。然而我都不会
\(T2\) 二维表
用搜索打个表
1, 2, 5, 14, 42,....
发现很眼熟啊,看到这个\(14,42\)我就知道绝对见过,仔细想,再仔细想。原来是\(Catalan\)数!
于是套公式\(Cat_n=\frac{C_{2n}^n}{n+1}\),对\(n!\)和\(2n!\)分解质因数,然后约分,最后跑高精乘法就行了。
可能是高精写挂或者是什么错误,我只拿了\(80'\)
\(T3\) 神秘数字
我最后再来写这题的,已经完全没时间了。赶紧写了个\(O(n^2)\)的暴力,枚举子串的起点,扫一遍。
一个子串的平均数大于\(m\),相当于把每个数减去\(m\),然后子串的和大于\(0\)。
所以我们把所有数都减去\(m\),然后求一遍前缀和,记为\(s\),若\(s[i]-s[j-1]>0\)则\(j\)~\(i\)这个子串是符合要求的。移项可得\(s[i]>s[j-1](i>j)\),于是归并排序求一遍“顺序对”就行了。
\(T4\) 地标访问
很水啊。
先按距离\(sort\)一遍,注意把原点加入数组,然后找到原点的位置,向左右两边分别走到每个距离小于\(T\)的点,然后往回走,看最远能走回到哪里,这个过程显然可以二分实现。
我用了\(STL\)
for(int i = cur + 1; i <= n && a[i] <= t; ++i){
int xs = lower_bound(a + 1, a + cur, (a[i] << 1) - t) - a;
if(xs >= cur) xs = 0;
else xs = cur - xs;
ans = max(ans, i - cur + xs);
}
for(int i = cur - 1; i && -a[i] <= t; --i){
int xs = upper_bound(a + cur + 2, a + n + 1, (t - ((-a[i]) << 1))) - a;
if(xs > n) xs = n - cur;
else xs = xs - 1 - cur;
ans = max(ans, cur - i + xs);
}
\(T5\) 最大因子和
这题\(10W\)以内我就直接暴力对每个数分解质因数算因数个数。如果大于\(10W\)我就模拟退火乱搞,比别人暴力多拿了\(9'\)qwq
正解是搜索?
打个\(30\)以内的质数表,然后枚举每个质因数的指数。有个数据会卡一下,特判一下就行了。
\(T6\) 最大间隙问题
题面说要我们设计一个线性算法,数据范围却让我们用\(sort\)?。。。????
反正水过就对了。
所以这次考试:\(0+80+40+100+54+100=374\)
海星吧。
9.30:改题,再说吧
9.28总结 & 9.29计划
昨天本来想去写《均分数据》的,但题解有个细节一直没看懂,就没去写了。写了《三步必杀》,然后去看了《HH的项链》,本来是想复习莫队的,结果数据已经被改数据狂魔\(\text{chen_zhe}\)改的莫队过不去了,但我看懂了主席树题解不亏,但没实现,打算今天实现。
昨晚的考试。再次爆炸
\(T1\) 加工生产调度
。。多少年前的毒瘤题了。。
我排序方法错了,然后就\(100\)->\(0\)了。
先对所有产品按\(min(a,b)\)升序排序,然后再从左到右扫一遍,如果\(a>b\)就放右边,否则放左边。
因为\(min(a,b)\)越小就代表这个产品能节省越多的时间,如果\(a\)小就放左边,\(b\)小就放右边。
\(T2\) 集合划分
题目不难,就是出数据的人爆\(int\)了,所以我们要跟着他爆\(int\)才能过
我用的记搜,时间复杂度\(O(2^n)\)
其实这题就和\(n\)个球放进\(n\)个盒子允许空盒是一样的,
递推方程:\(f[i][j]=f[i-1][j-1]+f[i][j-1]\),答案就是\(f[n][n]\),时间复杂度\(O(n^2)\)
\(T3\) 地毯填补
看起来有点复杂,加上旁边很多人都说没有\(spj\)过不了,我干脆了没写了qwq,考完一看,这不是很久之前写过的很水的分治吗我怎么一点印象也没有。亏了。
把正方形一分为四递归处理,公主在的子正方形会填满,其他三个各剩一个,集中在中心,然后再放一个就行了。
void fill(int size, int x, int y, int px, int py){
if(size == 1) return;
int X = x + size - 1, Y = y + size - 1;
int midx = (x + X) >> 1, midy = (y + Y) >> 1;
size >>= 1;
if(px <= midx && py <= midy){
printf("%d %d %d\n", midx + 1, midy + 1, 1);
fill(size, x, y, px, py);
fill(size, x, midy + 1, midx, midy + 1);
fill(size, midx + 1, y, midx + 1, midy);
fill(size, midx + 1, midy + 1, midx + 1, midy + 1);
}
if(px <= midx && py > midy){
printf("%d %d %d\n", midx + 1, midy, 2);
fill(size, x, y, midx, midy);
fill(size, x, midy + 1, px, py);
fill(size, midx + 1, y, midx + 1, midy);
fill(size, midx + 1, midy + 1, midx + 1, midy + 1);
}
if(px > midx && py <= midy){
printf("%d %d %d\n", midx, midy + 1, 3);
fill(size, x, y, midx, midy);
fill(size, x, midy + 1, midx, midy + 1);
fill(size, midx + 1, y, px, py);
fill(size, midx + 1, midy + 1, midx + 1, midy + 1);
}
if(px > midx && py > midy){
printf("%d %d %d\n", midx, midy, 4);
fill(size, x, y, midx, midy);
fill(size, x, midy + 1, midx, midy + 1);
fill(size, midx + 1, y, midx + 1, midy);
fill(size, midx + 1, midy + 1, px, py);
}
}
\(T4\) 金币排列问题
这不是很久前的那道毒瘤题《魔板》吗
算了,果断放弃
\(T5\) 雇佣计划
\(hkw\):这不是最小费用最大流吗
%%%
不会啊
我就写了个搜索骗了60分,海星。
炸了炸了炸了。
9.29:改题,写《HH的项链》,然后复习吧
9.27总结 & 9.28计划
昨天改了题,复习了一下二分图匹配,写了遍匈牙利算法,然后还学了模拟退火,写了平衡点/吊打XXX。
晚上是考试。
\(T1\) 取火柴游戏
裸的\(NIM\)游戏,算出异或和,若不为\(0\)则必赢,否则必败。
至于方案,设所有数的异或和为\(Xor\),然后枚举每一堆火柴\(a[i]\),如果\(Xor\ \text{xor}\ a[i] < a[i]\),把\(a[i]\)变成\(Xor\ \text{xor}\ a[i] < a[i]\)就好了。
其实就是让异或和变为\(0\),让对手必败。
\(T2\) 实数数列
样例都没看懂
\(T3\) 排序集合
点我
\(T4\) 平面上的最接近点对
没想到什么好方法,就写了个暴力,其实77分的暴力分已经很多了。
正解是分治,把所有点,按\(x\)坐标排序,然后找到\(x\)坐标的中位数,一分为二,算出左右两边的最短距离,然后合并的时候考虑在两边的点对就行了。
具体实现:
double Solve(int l, int r){
if(l == r) return 1e18;
int mid = (l + r) >> 1;
double Min = min(Solve(l, mid), Solve(mid + 1, r));
double tx = a[mid].x; top = 0;
for(int i = l; i <= r; ++i)
if(fabs(a[i].x - tx) < Min)
q[++top] = a[i];
sort(q + 1, q + top + 1, Cmp);
for(int i = 1; i < top; ++i)
for(int j = i + 1; j <= top && fabs(q[i].y - q[j].y) < Min; ++j)
Min = min(Min, dis(q[i], q[j]));
return Min;
}
\(T5\) 新汉诺塔
很久以前的毒瘤题了
思路很简单,就是从大到小依次移到目标柱上,如果比它小的在目标柱或在自己的上面,我先把这个比它小的移开,递归处理。
但我实现的时候出了点问题,只有\(30'\)
void move(int x, int y){
if(c[x] == y) return;
for(int i = x - 1; i; --i) move(i, 6 - c[x] - y);
printf("move %d from %c to %c\n", x, str[c[x]], str[y]);
--a[c[x]][0]; c[x] = y; ++a[y][0]; a[y][a[y][0]] = x; ++ans;
}
int main(){
Open("nhanoi");
scanf("%d", &n);
for(int i = 1; i <= 3; ++i){
scanf("%d", &a[i][0]);
for(int j = 1; j <= a[i][0]; ++j){
scanf("%d", &a[i][j]);
c[a[i][j]] = i;
}
}
for(int i = 1; i <= 3; ++i){
scanf("%d", &b[i][0]);
for(int j = 1; j <= b[i][0]; ++j){
scanf("%d", &b[i][j]);
e[b[i][j]] = i;
}
}
for(int i = n; i; --i)
if(c[i] != e[i])
move(i, e[i]);
printf("%d\n", ans);
return 0;
}
所以这次考试就是\(100 + 0 + 100 + 77 + 30 = 307\)。
9.27计划
该题,巩固模拟退火,写《均分数据》。
9.26总结 & 9.27计划
昨天去改前天的题了,然后就晚上了.
昨晚的考试。。。再次爆炸。
说实话这次考试不是很认真
\(T1\) 货物搬运
肛后面的题去了,导致这题暴力都没写,然而正解就是\(O(n^2)\)的暴力。
断环成链,然后跑\(n\)遍均分纸牌就行了。
\(T2\) 暴力摩托
...题都没看。被题名吓到了
当前的时间与以后的方案无关,说明这是无后效性的,可以\(DP\)。
\(f[i]\)表示走到\(i*10\)的最少时间,然后可以从\(i-1,i-2,i-3,i-4\)和\(1\)~\(i-5\)转移状态,时间复杂度\(O(n^2)\)
f[0] = 0; f[1] = 0.1; f[2] = 2.0 / 9.0; f[3] = 3.0 / 8.0; f[4] = 4.0 / 7.5; t /= 60.0; s /= 10;
for(int i = 5; i <= s; ++i)
f[i] = i * 1.0 / 7.0;
for(int i = 2; i <= s; ++i){
f[i] = min(f[i], f[i - 1] + 0.1 + t);
if(i > 2) f[i] = min(f[i], f[i - 2] + 2.0 / 9.0 + t);
if(i > 3) f[i] = min(f[i], f[i - 3] + 3.0 / 8.0 + t);
if(i > 4) f[i] = min(f[i], f[i - 4] + 4.0 / 7.5 + t);
if(i > 5)
for(int j = 1; j <= i - 5; ++j)
f[i] = min(f[i], f[j] + (i - j) * 1.0 / 7.0 + t);
}
\(T3\) 射击比赛
写了个优化的搜索,和大家一样只拿了\(27'\),而且大部分点都是\(WA\),应该是写挂了。
正解应该是二分图匹配。
点我
\(T4\) \(2^k\)进制数
这题看起来很复杂,但递推很好写。
显然是要写高精的,我写了高精,但有些地方纠结了很久。
转移数组本来是二维的,但很明显可以压成一维,但压成一维后每次都要对某一位清零,也就是说要\(memset\)一遍。
但不压维要开到\([30001][513]\)才保险,加上高精度数组的存在,显然会炸。
最后我压了维,但没写\(memset\),当时想错了,然后华丽丽的\(WA\)了,只得\(20'\),还没\(xjc\)的无高精\(50'\)高。加上\(memset\)后就\(80'\)了,最后两个点\(TLE\)。不过为什么这题只有我和xjc拿分?
然后我又疯狂卡常,疯狂压位高精,硬是把最后两个点卡过去了。不过洛谷上跑的要快很多,最慢的点也就用了\(100ms\)。
\(f[i][j]\)表示从右往左第\(i\)位为\(j\)的情况,那么就有\(f[i][j]=\sum_{p=j+1}^{2^k-i}f[i-1][p]\)
第一维显然可以压掉。
\(T5\) 地图
最水的一题,大法师标记连通块就好了。
9.26计划
改题,学模拟退火
9.25总结 & 9.26计划
昨天改了9.23的题,然后还写了《软件安装》
晚上是考试
\(T0\) 分球
欲哭无泪,很经典的球放盒子问题,然而我不会,最后写了个很暴力的暴力,打表找出了规律,但因没写高精只有20分。。。
递推式:$$f[n][m]=f[n-1][m-1]+f[n-1][m]\times m$$
(这是我打表找出的规律,也是正解)
考虑从\(f[n-1][m]\)转移,第\(n\)个球有两种选择:
1、在原来的基础上随便放一个位置,就是\(f[n-1][m]\times m\)
2、单独占一个盒子,其他\(n-1\)个球放在\(m-1\)个盒子里,就是\(f[n-1][m-1]\)
\(T1\) 求和
欲哭无泪\(\times 2\)
考场也是找到规律了的,但还是因为细节问题惨失70分。
容易发现\(a^b=(a\mod 10000)^{b\mod 10000}\)
所以,预处理出\(1^b+2^b+...+10000^b\mod 10000\)
然后
printf("%d\n", (a / MOD * f[MOD] + f[a % MOD]) % MOD);
就好了
\(T2\)
点我
\(T3\)
暴力题一个,然而我直接放弃了。
若同一个点的坐标为\((X,Y)\),则\(n\)个点的答案为\(\sum_{i=1}^n|x_i-X|+|y_i-Y|\)
可以发现\(x\)与\(y\)没有关系,可以分开来看,变成\(\sum_{i=1}^n|x_i-X|+\sum_{i=1}^n|y_i-Y|\)
于是变为两个一维的问题,显然,放在中位点会更优,所以我们要求的中心点的\(x,y\)坐标都是现有的\(x,y\)坐标,\(O(n^2)\)枚举中心点,\(O(n)\)统计答案,再\(O(n)\)更新答案就好了。总时间复杂度\(O(n^3\log n)\)
int dis(int a, int b){
return abs(a - X) + abs(b - Y);
}
int main(){
memset(ans, 0x3f3f3f3f, sizeof ans);
Open("tower");
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d%d", &x[i], &y[i]);
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= n; ++j){
int sum = 0;
X = x[i]; Y = y[j];
for(int k = 1; k <= n; ++k)
p[k] = dis(x[k], y[k]);
sort(p + 1, p + n + 1);
for(int k = 1; k <= n; ++k)
ans[k] = min(ans[k], sum += p[k]);
}
}
for(int i = 1; i <= n; ++i)
printf("%d\n", ans[i]);
return 0;
}
\(T4\)
9.23总结 & 9.25计划
今天上午考试
下午就放假了
自己都没想到,就虐场了
\(T0\) 产品排序
J哥说是送分题,然而我们并不这么觉得,毕竟只有2个人A了
。。我的玄学搜索+贪心优化+卡时直接A了,速度还比正解\(DP\)(Zgx)快了很多倍?
试想,若一个机器加工的产品是确定好的,那么显然把冷却时间长的放在前面加工要更优,所以我们把所有产品按冷却时间降序排序,然后依次放,这样就省去了统计答案的时间。
于是开始搜索,每个产品有两个选择:
1、让\(1\)机器加工
2、让\(2\)机器加工
直接搜时间复杂度是\(O(2^n)\),而\(n\)最大是\(200\),显然过不了。所以考虑优化。
1、最优性剪枝
2、贪心优化搜索顺序
3、卡时
void dfs(int now, int sum1, int sum2, int p1, int p2){ //sum1是机器1的加工时间,p1是机器1的总时间,sum2、p2同理
if(max(p1, p2) >= ans) return; //最优性剪枝
if(now == n + 1){ ans = max(p1, p2); return; }
if(++Time >= 150000) printf("%d\n", ans), exit(0); //无敌的卡时,如果你问为什么15W就停?因为我怕爆栈
if(p1 < p2){ //贪心优化搜索顺序,优先往当前时间少的机器放,尽量让2个机器平均
dfs(now + 1, sum1 + s[now].a, sum2, max(p1, sum1 + s[now].a + s[now].b), p2); //放在机器1
dfs(now + 1, sum1, sum2 + s[now].a, p1, max(p2, sum2 + s[now].a + s[now].b)); //放在机器2
}
else{
dfs(now + 1, sum1, sum2 + s[now].a, p1, max(p2, sum2 + s[now].a + s[now].b));
dfs(now + 1, sum1 + s[now].a, sum2, max(p1, sum1 + s[now].a + s[now].b), p2);
}
}
为什么这么卡时能得到正确答案?
我们贪心优化了搜索顺序,使得每次搜到的都是一个比较优的解,所以那些不优的会被直接剪枝剪掉,15W次足够我们搜到很多优解,最优解也就有极大可能在其中。
神不神奇?
\(T1\) 倒水
思维难度不大。
类似与2048的东西,容易想到二进制,\(n\)的二进制位上有多少个\(1\)就说明最后至少合并成多少个瓶子,我们就是要对\(n\)加上一个数,使得\(n\)的二进制位上只有至多\(k\)个1。
贪心乱搞就好了。伪代码如下
while(1的个数>k){
找到进位所需的数最小的1,使之进位,并统计答案
}
while(xbq_is_shouting){
if(sum <= k) break;
now = 0; Min = 2147483647; sum = 0;
for(int i = 0; i < w; ++i)
if(n & (1 << i)){
++sum;
if((1 << i) - now < Min)
Min = (1 << i) - now;
now &= (1 << i);
}
n += Min; ans += Min;
}
\(T2\) 单词缩写
正解不会啊
写了搜索居然搜了\(80'\)
正解是\(DP\),设\(f[i][j][k]\)表示第\(j\)个单词第\(k\)个字母作为缩写第\(i\)个字母的方案数,
从\(f[i-1][j-1][k](0<k<len_{j-1})\)和\(f[i-1][j][k'](0<k'<k)\)转移就好了
\(T3\) 求和
不可做题。。高精减除乘都有。。哎算了\(还是拿暴力分吧\)
\(T4\) 象棋比赛
贪心水题嘛。
先按实力排序一遍,再把实力相邻的两个人的差再升序排序一遍,然后顺着取就好了。显然是最优的。
于是390'虐场
其实我只是把该拿的分拿了,稳点就行了。
\(9.25\)计划:
改题,写《软件安装》《矿场搭建》
9.22总结
今天上午是考试。。
\(T1\) 购物
贪心,然而我并没有想出来,写了个错误的时间复杂度极高的贪心,水了40分。
如果可选面值里没有\(1\),显然是无解的,否则一定有解。
定义一个变量\(sum\)表示当前已经能拼出的最大面额,初始为\(0\),
然后
while(sum < x){
for(int i = n; i; --i)
if(sum + 1 >= a[i]){
sum += a[i];
break;
}
++ans;
}
如果当前拼不出下一个数,就找到最大的小于等于下一个数的面额\(a[i]\),那么显然\(sum+a[i]\)以内都是可以拼出了的,因为\(sum\)以内是已经确定可以拼出的了,所以\(a[i]+1,a[i]+2,...,a[i]+sum\)都是可以拼出的了。
\(T2\) 养猪
并没有想到这是一个背包。。仍然贪心乱搞,不记得多少分了。
如果体重不会减少,这显然就是个裸的\(01\)背包,但这里体重会减少,也就是说每件物品的价值不是恒定不变的。
关于为什么不能直接跑\(01\)背包,个人觉得这是个有思维难度的问题,因此,我找到一篇讲的比较好的博客
按\(P\)值降序排列,然后跑\(01\)背包就好了。因为体重减少快的先取显然更优。
\(T3\) 数位平方和
我因为常数大只得了\(50points\)。。
看到题目给的函数就想到肯定存在环,直接照着题目写函数的话显然是会无限递归的。这个环上所有数的函数值是一样的,于是想到记忆化。体重\(k\)的最大值为\(6\),也就是说\(S(x)\)的最大值为\(7*9^6\),\(H\)还要更小,显然能直接用数组存下。
于是就有
int H(int x){
if(h[x]) return h[x];
if(vis[x] >= 2)
return 1000000000; //返回一个最大值,也就是放弃参与min运算
vis[x]++;
return h[x] = min(x, min(S(x), H(S(x))));
}
为什么要访问过两次再停止呢?因为若第一次遇到访问过的就停止的话,后面的数的值就不会是整个环的最小值,而是一个“后缀最小值”,于是我们要在环上走两遍来更新环上所有数。
\(T4\) 扩散
思路有了,但我考场时连边的边权想错了。。
\(O(n^2)\)暴力枚举所有点对,连一条边权为两点的曼哈顿距离除以二向上取整的边,然后跑一遍最小生成树就好了。
9.20总结 & 9.21计划
昨天又是大部分时间用去改题了。。敲砖块那题感觉所有人都讲的不清楚,(包括qzq),他们都不知道读者的疑惑所在,我也是无意中看到一篇比较好的博客才理解了。
然后去做《三步必杀》,没写完就开始考试了。。
\(T1\) 序列
开始以为是贪心,(事实证明确实是贪心),然而并想出怎么贪,然后就觉得不是贪心,然后就放弃了2333,看来我还是不善于做这种区间覆盖问题。
%%%
可以贪心,按右端点排序,然后从右往左取即可,这样可使重复覆盖的次数最多。
但我现在用差分约束做的,
点我
\(T2\) 矩形
很简单的一题。。
这似乎是我做的最谨慎的一题了,,
我算好时间复杂度\(O(n^2m\log n)\),最坏情况大概是几千万,然后写完了我还写了个暴力,写了个脚本对拍了下,全"AC",然后才放心。
枚举所有点\((i,j)\),再枚举子矩阵的高度,然后二分找到最后一个全为\(0\)的列\(p\),则\(ans+=p-j+1\)。
\(T3\) 锁
显然是道不可做题。
\(T4\) 看守
\(O(n^2)\)暴力搞了30分。。
点我
9.21 改题,复习\(Tarjan\)各种缩点(e-dcc,v-dcc,scc)
9.19总结 & 9.20计划
昨天改完题就去写月赛T3快递员
晚上又是\(KS\)。原地爆炸。
\(T1\) 中位数
没想到什么巧妙的方法,只好去写60分的暴力,于是就用主席树写了个\(O(pos_b(n-pos_b)\log n)\)的暴力,其实可以直接做到\(O(n^2)\),但我想练一下主席树,而且\(n=1000\)都是稳过的。
最后得了60分。
其实我们之关心数的相对大小,于是,把左边大于\(b\)的用\(1\)表示,右边小于\(b\)的用\(1\)表示,左边大于\(b\)的用\(-1\),右边大于\(b\)的用\(-1\)表示,然后就是要求包含\(b\)的和为0的子序列的个数了。对\(b\)左边和右边分别求前缀和,同时统计每个值出现的次数,枚举前缀和的所有取值,然后乘起来求和就好了。
\(T2\) 敲砖块
直接放弃了。
点我
\(T3\) 合并序列
水题一个,直接\(string+sort\)就能水过,但我还是用了字典树,结果,我全程就用一个字符数组,不停的覆盖,然后遍历的时候后面没清空,\(WA\)了。
已改。
\(T4\) 邮递员送信
虽然做错了,但也收获不少。
我是直接跑了\(n\)遍\(Dijkstra\),然后华丽丽的\(TLE\).
其实只要先对\(1\)号点跑一边\(Dijkstra\),然后建反图,再对\(1\)号点跑一遍\(Dijkstra\)就是其他所有点到\(1\)号点的最短路了。
9.20计划:改题,刷洛谷试练场\(DP\)
9.18总结 & 9.19计划
昨天把前天的题目改完,然后完善了一下博客,就到晚上了(毕竟紫题不是那么简单的)。
昨晚的\(KS\)又炸了,不过感觉这次考试分化不大,感觉\(T1,T2\)都能写出来,\(T3\)都能拿暴力的50分,(\(orz\ qzq\)切掉了\(T3\))
\(T1\) 漂亮子串
特判的时候写错了,我判成:只要有一个数是\(0\)就输出\(0\),事实上如果只有一个是\(0\)的话另一个也是可以放的。改过来就\(A\)了。
思路也很简单,就是个贪心题。用最多能放的个数除以每段能放的个数向上取整就得到最少要几段才能放完,这个段数少的一定能放完,然后段数多的尽量放就好了。
\(T2\) 集合
几乎没有思维难度。
线筛\(b\)以内的质数,然后找到第一个大于\(p\)的,开始枚举,类似欧拉筛的方法,把\([a,b]\)内这个质数的倍数都用并查集连起来,最后扫一遍有多少个集合就好了。
\(T3\) 释放囚犯
考试时感觉这可能是个贪心,然而一直没搞出来,最后写了个搜索,拿50分。
正解是\(DP\)
正着做不好做,不如反着想,把出狱看成断链,反过来就变成合并石子了。
\(T4\) 树的匹配
不可做题。
9.19计划:
改题,写月赛T3。
9.17总结 & 9.18计划
上午主要是讲&改这7天没改出来的题。
我把《子矩阵》改了。
然后去看树套树,有点复杂。然后去学可持久化线段树和主席树。
把模板题给做了。
然后就到晚上了。
今晚的考试是\(NOIP\)模拟题,
\(T1\)
数7.
想不到什么优化,只能暴力,而暴力呢90分都拿不到,只好用分段打表法,用48秒生成了100个10000000的倍数的表,然后从最近的开始跑,最多跑10000000次,稳稳的\(A\)了。
\(T2\)
正方形计数.
一直在肛这个题,感觉可以\(O(n^2)\)做出来,因为对角线确定了正方形肯定是唯一的,但一直没想出来怎么通过对角线求正方形的另外两个点的坐标,时间浪费了太多,于是只好\(O(n^3)\)枚举对角线,再枚举另外一个点,算出最后一个点。拿了40分。
现在知道知道对角线求另外两点的公式了:
int midx = (x[i] + x[j]) / 2;
int midy = (y[i] + y[j]) / 2;
int x1 = midx - (midy - y[i]), y1 = midy + (midx - x[i]);
int x2 = midx + (midy - y[i]), y2 = midy - (midx - x[i]);
\(T3\)
前缀单词
显然又不会做,直接暴搜,用\(string.find\)预处理出每两个单词能不能共存,然后搜索,\(50points\)
正解
\(T4\)
数列
明显的不可做题啊,\(100‘’\text{的数据满足}n<=10^5\),正解显然是\(O(n)\)或者\(O(n\log n)\)的,时间也不多了,继续暴搜,\(O(n^2T)\),\(T\)为操作次数。拿了30分。
事实证明,的确是道不可做题,点我
虽然一道难题都没切,但这次是少数的没出现失误的考试,该拿的暴力分都拿到了,最后的分数是\(100+40+50+30=220\),并不是很难看,至少也是\(Rank2\)。
所以,\(9.18\)的任务当然是改题啦。改好题目我会来更新试题的总结。