停课刷题记录

CSP冲刺刷题记录

10.14

[TJOI2017]异或和

求所有区间和的异或和

处理前缀和

对每一位而言,看是否发生相减得1的情况,总之要看几种相减借位的情况

kth kth
\(Si\) 1 $ S_i $ 1
\(Sj\) 1 \(S_j\) 0
\(S_i\) 0 $ S_i $ 0
$ S_j $ 1 $ S_j $ 0

用树状数组维护即可

细节题,需要维护前缀和为0,所以树状数组下标整体右移一位

noip2016组合数问题

杨辉三角+矩阵前缀和

他这个前缀和有点怪,总之wa了一次

我把第0行和第0列忽略了,总之记住递推的起始条件:
\[ C_n^0 = 1, C_0^0 = 1 \]

noip2003 加分二叉树

自己想到30%的样子

这个算法是自下而上式的,令人欣慰的有序状态空间

利用了区间dp的思想,用 $f[i][j] $表示i到j所组成的子树的最优子结构

想一下二叉树的中序遍历的性质:只有左边是右边的左儿子,和右边是左边的右儿子的情况。

二叉树的分取决于谁是根:所以枚举根,左子树和柚子树自然也确定了
\[ f[i][j] = MAX\{ f[i][k-1]*f[k+1][j] + f[k][k] \} \]

注意$ root[i][i] = i $置初值

[LnOI2019SP]龟速单项式变换(SMT)

有如下定义:若正整数序列aa中存在连续若干个正整数的和为m的倍数,则这个正整数序列a被称为“司m序列”。

给定nm,你需要知道长度为n的任意正整数序列a是否都是"司m*序列"。

神秘居然让我试出来了,首先\(n < m\)一定能构造出司m的反例,比如全填1

正解是鸽巢原理:

长度为\(n,(n\ge m)\)的序列:求出他的前缀和\(sum[i]\)

对于这个单调增的序列,里面的取值可以有很多情况,最极端可以有n个值互不相同

由于有大于等于 m 个值,所以在模m环境下一定有两个的值相同

故一定存在\(sum[i] \equiv sum[j] (mod\ m)\)

则存在\(sum[i]-sum[j] \equiv 0 (mod\ m)\)则他是一个司马序列

10.16

[LnOI2019SP]快速多项式变换(FPT)

一道有意思的构造题,贪心利用高位,最后剩下的放到\(a_0\)就可以了

把题搞错了发现他\(n\le100\)\(a_i < m\)这就每一项都需要构造

出题人题解:

不难发现,设\(a_na_{n-1}\cdots a_2a_1a_0\)是一个m进制数,将此数转换为10进制即为f(m)的值。因此只需要倒序输出f(m)在m进制下每一位的值即可。

满足条件的多项式可以证明只有一个

直接取模倒序输出

hanoi

n阶m塔汉诺塔问题

现在搞出了\(O(n^2m)\)的算法:
\[ f[n][m] = min\{ 2\times f[k][m]+f[n-k][m-1] \} \]
从2,3开始循环,前面的可以直接置答案,然后不会了

据说单调指针优化可以有\(O( n^2 )\)

颓了一下:把Python简单学了一下

然后A了国王游戏

定义类,自定义比较函数

class Node:
    def __init__(self,a,b):
        self.a = a
        self.b = b
def _cmp(a,b):
    if a.a*a.b>b.a*b.b: return 1
    return -1

定义数组

lis = []

输入需要split,而且还要强制转类型,不然他以为这个是string

for i in range(n): #这里是从1到n
    s = raw_input().split()
    lis.append(Node(int(s[0]),int(s[1])))

自带指针:

for i in lis:
    #访问 i.a 以及 i.b

三角形牧场

交了12次

把搜索能犯得错都犯了

枚举每个木棒在哪个边

要判三角形是否合法,而且不能一次性把6种排列一次hash,因为有的等价答案可能扩展出其他不同的答案,要加只加可行性剪枝和重复性剪枝,重复性剪枝保证减掉的是完全相同的枝

判三角形是否合法

if(a+b>c && a+c>b && b+c>a) return true;
return false;

再用海伦公式时:所有参数必须以double形式参与运算总之我出了精度的锅

神秘乱搞题:子序列

给定一个长度为N(N为偶数)的序列,问能否将其划分为两个长度为N/2的严格递增子序列

正解是dp看不懂,但是我们写了乱搞,找到了充分不必要条件,神秘总之过掉了

LDS >= 3就是no,这个好证,如果有下降的两个数必定在两个序列,来了第三个就死了。

然后有hack数据

4 4 1 2 3

6 6 1 2 3 4 5

6 1 6 2 3 4 5

容易特判 。

掏题解

会议

树形dp水题

大概就是求所有点到某个点路径和最小

经典换根dp,套路就是先收回根然后下放下去得到每个点为根时的答案
\[ f[x] = \sum_{v \in son } f[v] + size[v] \\ 下放:f[v] = f[x] - size[v] + (n-size[v]) \]
还没有涉及到求最值,找时间复习一下在成都的那道题

逃离僵尸岛

图论水题

把一个点extend一下,然后点权为边权

dij一下,第一次交时候数组开小了绝了

10.17

豪华游轮

dfs20分

正解贪心配合dp

就是先用forward冲,然后转180度,那backward变成朝原来方向冲,虽然不一定凑出180度但是尽量靠近

背包凑180度,可达性01背包,我跑了两次,分别是把方向正负换了一下的,求最靠近180的dir

最后用了一下三角函数
\[ 弧度 = \frac{角度*\pi}{180} \\ \pi = acos(-1) \]
对于backward:
\[ x = x-dis\times cos(dir)\\y = y-dis \times sin(dir) \]

单词背诵

hash,hash_table,以及尺取法

初始化l=r=1,加入集合只是r指针的操作,移除集合只是l指针的操作,在合法期间更新ans

10.20

停课开始了

今天上午去医院了,下午来补考试,然后做了超级久,最后一个没摸出来,但是前面的两道题都没挂

就要保持这个心态

然后学了一下prufer

[HNOI2004]树的计数

\[ ans = \frac{(n-2)!}{\prod_{i=1}^{n} (d_i - 1)!} \]

注意乘爆,注意树不连通即di = 0,以及n=1是d1只能等于1否则无解

计数类好题

CF915E Physical Education Lessons

本来是区间赋值区间求和的裸题,但是n达到了1e9

用珂朵莉

然后相当于复习了一下

维护01的sum根本就不用写个sum函数,直接在assgin里面维护就可以了,可以做到O(assgin)维护

最后时间复杂度也是省了一倍

10.21

今天考了一套题非常好,以后要翻出来看

晚上做了一道dp

[USACO08FEB]修路Making the Grade

求把整条路通过加高变矮形成非严格单调增或者非严格单调减,最小费用

拓宽了我的视野:dp状态的 数学概况力 以及 泛化状态表示

根据贪心,所有修改后的值都是在序列里存在过的值

离散化,\(n^2\)空间,\(f[i][j]\)表示第i个,高度为j时的最优价值

由于状态一定从i-1层的最优的状态转移而来,在循环j的时候用一个变量保存当前上一行的最优值,这个思路和LCIS有点像,今天才做过

10.22

[JLOI2011]飞行路线

分层图模板题

分层图的一般模型:

 在图上,有k次机会可以直接免费通过一条边,问起点与终点之间的最短路径.

用二维的dis

\(dis[i][j]\)代表到达i用了j次免费机会的最小花费.

\(vis[i][j]\)代表到达i用了j次免费机会的情况是否出现过.

裸的dijkstra转移即可:

Auto(i,x)
{
    int v = e[i].v;
    if(j+1<=k && dis[v][j+1] > dis[x][j])
    {
        dis[v][j+1] = dis[x][j];
        q.push((node){dis[v][j+1],v,j+1});
    }
    if(dis[v][j] > dis[x][j] + e[i].w)
    {
        dis[v][j] = dis[x][j] + e[i].w;
        q.push((node){dis[v][j],v,j});
    }
}

注意最后的答案是\(dis[ed][i]\) 的最小值

(x倍经验)[BJWC2012]冻结

k次优惠变成了费用减半

(x倍经验)[USACO09FEB]改造路Revamping Trails

一样的模板,但是k等于20我只开了20数组改半天

(x倍经验)[USACO08JAN]电话线Telephone Lines

这个题要求的是最短路径上最长边,这么写就可以了

if(dis[v][j] > max(dis[x][j], e[i].w) && !vis[v][j])
{
    dis[v][j] = max(dis[x][j], e[i].w);
    q.push((node){dis[v][j],v,j});
}

[USACO08JAN]跑步Running

这个dp依然提倡一个思路:在合理复杂度下的泛化状态表示,能表达的状态越广泛,dp思路的成功率越高

这个题就是如此,用\(f[i][j]\)表时间为i,疲劳度为j的最优解,

同时又必须注意两大限制,所以同时用了刷表和填表法

必须从\(f[0][0]\)开始转移

\(f[i][0]\)必须有前面的\(f[i-p][p]\)转移,所以它我就用了前面的状态刷表,然后这个状态的0可以由\(f[i-1][0]\)转移,这么写起就稍稍有点乱

否则普遍由\(f[i-1][j-1]+d[i]\)转移而来

一遍AC,rp++

    memset(f,-1,sizeof f);
    f[0][0] = 0;
    for(int i=1;i<=n;i++)
    {
        f[i][0] = max(f[i][0],f[i-1][0]);
        for(int j=1;j<=m;j++)
        {
            if(f[i-1][j-1]!=-1) f[i][j] = max(f[i][j], f[i-1][j-1] + d[i]);
            if(i+j<=n) f[i+j][0] = max(f[i+j][0], f[i][j]);
        }
    }
    io << f[n][0];

10.23

[SCOI2010]连续攻击游戏

基本上所有题解都在误导,这个题应该把点集分开成两个域,但是几乎所有题解都在把两个点集挼成一个来做。

让属性成为\(S\)集合,装备为\(T\)集合,让匈牙利从1往10000匹配,只能増广否则终止,这是因为当\(dfs(i)\)返回0时意味着i没有匹配的点。要求的匹配一定连续

然后这个边建单向的双向的都可以,但是这个题的理解是建单向边,表示属性对应的装备

总之这个题搞了超级久,加深二分图理解

10.24

最长路_NOI导刊2010提高(07)

有向图上的问题,且与环有关的问题都可以用拓扑排序搞事情

首先如果拓扑排序结束后仍然有点没有入队,证明有环

对于求指定两点的最长路,可以把所有点的\(dis\)置为\(-inf\)然后把\(dis[st]\)置为0然后开始做拓扑排序dp

它本质上是一个无后效性的dp

杂务

求拓扑序里面的最后结束工作的时间,

还是dag上最长路

点权化边权,可以加超级原点超级终点

最小函数值

和那个序列合并有点像都是用堆得前m小的值

如果弹出这个函数的值,那么把x+1的函数值加入堆,就没有了

[HNOI2003]操作系统

啊啊啊模拟题弄昏我 昏睡模拟

这个端点很迷,好像写的时候根本没管它一样

用一个优先队列模拟这个cpu的等待队列

每次时间锁定在下一个进程到来之前,然后利用时间间隙向右推进

当然优先队列按照优先级和出现时间排序

充分利用下一个进程之前的时间,把能完成的都完成了

然后有一个即将被换掉的进程,把他的时间给减了,然后放回堆里,下一优先级高的自然在堆里把他压下去了

最后剩下一堆进程,按优先级加时间即可

    while(scanf("%d%d%d%d",&nxt.id,&nxt.st,&nxt.rest,&nxt.pri)!=EOF)
    {
        while(!q.empty() && nowtime + q.top().rest <= nxt.st)//在下一个进程来之前可以完成等待队列的进程 
        {
            node x = q.top(); q.pop();
            nowtime += x.rest;
            io << x.id << ' ' << nowtime << '\n';
        }
        if(!q.empty())//在下一个进程到来之前
        {
            node x = q.top(); q.pop();
            x.rest -= nxt.st - nowtime;//还有时间可用
            q.push(x); 
        }
        q.push(nxt);
        nowtime = nxt.st;
    }
    while(!q.empty())
    {
        node x = q.top(); q.pop();
        nowtime += x.rest;
        io << x.id << ' ' << nowtime << '\n';
    }

TJOI2009]开关

线段树区间异或 区间求和

靠是模板题

tag可以^=1这样两次之后相当于不反转,非常省时间

然后区间反转就是sum = len-sum

我觉得挺好看的

inline void pushdown(int rt)
{
    if(tag[rt])
    {
        t[ls].sum = t[ls].len - t[ls].sum;
        t[rs].sum = t[rs].len - t[rs].sum;
        tag[ls] ^= 1,tag[rs] ^= 1;
        tag[rt] = 0;
    }
}

贪婪大陆

又是一道经典神题!lsq震撼我妈秒切此题

有很多不同的线段覆盖在区间上,查询一段区间有多少不同的线段覆盖在上面,注意这里的线段可以重叠覆盖。

这个模型是以前见过的,今天起给我永远记住它:求区间相异线段(区间)覆盖数

用右端点左边的起始点数 减去 左端点左边的结尾数 = 区间覆盖数

相当于用所有右端点左边的开始的区间数 - 所有未延伸到左端点内的区间数 得到所有在区间内的数

在这个数学模型里,我们把所有散乱的线段化作一条条射线,横冲直撞地冲向右边,而我们只需要截下所有冲过右端点的线段,而那些在左端点前及时停下的射线我们就可以减去了

直接用树状数组维护即可

io >> op >> l >> r;
if(op == 1) st.add(l,1),ed.add(r,1);
else io << st.query(r) - ed.query(l-1)  << '\n';

猜你喜欢

转载自www.cnblogs.com/tythen/p/11745574.html
今日推荐