今天是钟皓曦老师的讲授~

今天的题比昨天的难好多,呜~

T1

我们需要找到一个能量传递最多的异构体就好了;

整体答案由花时间最多的异构体决定;

现在的问题就是这么确定一个异构体在花费时间最优的情况下所花的时间是多少;

我们去枚举一个异构体 i,以这个异构体为分界线将其分成左右两部分,设左半部分的异构体的能量和为 sum;

如果 sum > ( i-1 ) * v,v * n - sum(右部分) < 0 ,说明左边的能量是够的,不需要再输入能量了,但是右部分却不够,所以我们需要从左边往右边输送能量,那么输送的能量的多少就是 i 这个异构体的贡献;

做法:去枚举每一个 i,考虑这个节点它的作用(往哪里传递能量) ,注意一共四种情况:

1. 左边多,右边少;那么 i 节点的作用就是把左边多余的能量传递到右边;

2. 左边少,右边多;那么 i 节点的左右就是把右边多余的能量传递到左边;

3. 左边多,右边也多;也就是说 i 这个点少了好多能量,我们就要把两边多余的能量搬到 i,时间取决于耗时长的一遍:max(x,y);

4. 左边少,右边也少;也就是说 i 这个点多了好多能量,我们要把多余的能量搬到两边,但是注意每一时刻只能搬一点能量,所以需要 x+y 的时间;

所以这个题就做完了;

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm> using namespace std; const int maxn=; int n,z[maxn]; long long sum[maxn]; void read()
{
scanf("%d",&n);
for (int a=;a<=n;a++)
scanf("%d",&z[a]);
} long long work()
{
for (int a=;a<=n;a++)
sum[a] = sum[a-]+z[a];
if (sum[n]%n) return -;
long long ans=,v=sum[n]/n;
for (int a=;a<=n;a++)
{
long long l=sum[a-]-1ll*(a-)*v;
long long r=sum[n]-sum[a]-1ll*(n-a)*v;
if (l< && r<) ans=max(ans,-l-r);
ans=max(ans,max(abs(l),abs(r)));
}
return ans;
} int main()
{
read();
printf("%lld\n",work()); return ;
}

T2

语文太差了,还以为必须要从 1 走到 n,然后 100 -> 0(其实是玄学 RE);

没要求必须从左往右走?炸了~

我们从小到大排好序,这样的话我们选数的时候只需要考虑所选的数是不是前面的数的倍数就行了(因为前面的数一定是更前面所有数的倍数);

f [ i ] 表示已经取了第 i 个数了,我们去枚举前面选的那个数是多少,看是不是它的倍数就可以了:

f [ i ] = max ( f [ j ] ) + 1,a [ i ] 是 a [ j ] 的倍数;

这个题搜索写得好好像可以过。。

我们看到有个很重要的隐藏 bug:有 20% 的数据保证所有城市的数都不重复;

考虑到由于所选的数都是倍数关系,所以这些城市的数最多就 20 个不同的数!

那么我们可以先去重,然后如果一个数 a [ i ] 被选上了,那么和它相等的数也都要选上,这样才能保证最优嘛;

所以我们可以在此基础上搜索,或者按照上面说的那样 dp 一下,就可以过了;

然而不是正解?

正解:

我们可以开一个桶,记录每个数出现了多少次;

改造一下 dp:f [ i ] 我取出来的序列最后一个数等于 i 的情况下最长是多少;

那么接下来我们要取得的是 i 的倍数,所以我们就去枚举 i 的倍数,一直枚举到一百万就行了;

f [ k * i ] = max ( f [ i ] + cnt [ k * i ] );

我们不关心那个数是多少,我们只关心有多少个 。

#include<cstdio>
#include<cstdlib>
#include<cstring> using namespace std; const int maxn=; int n,cnt[maxn],f[maxn]; int main()
{
scanf("%d",&n);
for (int a=;a<=n;a++)
{
int v;
scanf("%d",&v);
cnt[v]++;
f[v]++;
}
int ans=;
for (int a=;a<=;a++)
if (f[a])
{
if (f[a]>ans) ans=f[a];
for (int b=a+a;b<=;b+=a)
if (cnt[b] && f[a]+cnt[b]>f[b]) f[b]=f[a]+cnt[b];
}
printf("%d\n",ans); return ;
}

T3

zhx:明天考试不考 2-SAT,真香~

最小值最大化,我们用二分;

我们二分答案 v,那么我们所选的任意两张卡牌的差值的绝对值都要大于 v;

假如我们有两组卡牌,他们的战斗力分别是 a1 , b1 和 a, b2

如果 a- a2 < v,那么说明 a1 和 a2 不能同时选,所以如果我们选了 a1  ,那么我们就只能选 b2 了,从 a1 向 b2 

这样能过 50% 的数据;

考虑到建边的时间复杂度是 O ( n2 ) 的,显然过不了 100% 的数据;

线段树优化建边 。

我们将这 2n 个数从小到大排序,假如现在我们考虑 ci ,需要和它建边的点一定是一个连续的区间;

发现这是一个区间加边的操作,怎么搞呢?

我们建一棵线段树,然后对线段树进行区间加边的操作,我们可以把线段树上的边也看做是图的一部分的话,那么我们就只需要建 log n 条边:

每次只需要加 log n 条边,总时间复杂度 O ( n log 2 n );

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<algorithm> using namespace std; const int BUF_SIZE = ;
char buf[BUF_SIZE], *buf_s = buf, *buf_t = buf + ; #define PTR_NEXT() \
{ \
buf_s ++; \
if (buf_s == buf_t) \
{ \
buf_s = buf; \
buf_t = buf + fread(buf, , BUF_SIZE, stdin); \
} \
} #define readint(_n_) \
{ \
while (*buf_s != '-' && !isdigit(*buf_s)) \
PTR_NEXT(); \
bool register _nega_ = false; \
if (*buf_s == '-') \
{ \
_nega_ = true; \
PTR_NEXT(); \
} \
int register _x_ = ; \
while (isdigit(*buf_s)) \
{ \
_x_ = _x_ * + *buf_s - ''; \
PTR_NEXT(); \
} \
if (_nega_) \
_x_ = -_x_; \
(_n_) = (_x_); \
} #define readstr(_s_) \
{ \
while (!isupper(*buf_s)) \
PTR_NEXT(); \
char register *_ptr_ = (_s_); \
while (isupper(*buf_s) || *buf_s == '-') \
{ \
*(_ptr_ ++) = *buf_s; \
PTR_NEXT(); \
} \
(*_ptr_) = '\0'; \
} #define readlonglong(_n_) \
{ \
while (*buf_s != '-' && !isdigit(*buf_s)) \
PTR_NEXT(); \
bool register _nega_ = false; \
if (*buf_s == '-') \
{ \
_nega_ = true; \
PTR_NEXT(); \
} \
long long register _x_ = ; \
while (isdigit(*buf_s)) \
{ \
_x_ = _x_ * + *buf_s - ''; \
PTR_NEXT(); \
} \
if (_nega_) \
_x_ = -_x_; \
(_n_) = (_x_); \
} #define wmt 1,(n<<1),1
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1 const int maxn=;
const int maxp=maxn+(maxn<<);
const int maxm=maxn+maxp+maxn*; int n,size,cnt,en,t,dfn[maxp],low[maxp],s[maxp],belong[maxp],pos[maxn]; bool instack[maxp]; struct edge
{
int e;
edge *next;
}*v[maxp],ed[maxm]; void add_edge(int s,int e)
{
en++;
ed[en].next=v[s];v[s]=ed+en;v[s]->e=e;
} struct rec
{
int v,p;
rec(){}
rec(int a,int b)
{
v=a;p=b;
}
}z[maxn]; bool operator<(const rec &a,const rec &b)
{
return a.v<b.v;
} void dfs(int p)
{
t++;
dfn[p]=low[p]=t;
instack[p]=true;
s[++size]=p;
for (edge *e=v[p];e;e=e->next)
if (!dfn[e->e])
{
dfs(e->e);
low[p]=min(low[p],low[e->e]);
}
else
{
if (instack[e->e]) low[p]=min(low[p],dfn[e->e]);
}
if (dfn[p]==low[p])
{
cnt++;
while (s[size]!=p)
{
belong[s[size]]=cnt;
instack[s[size]]=false;
size--;
}
belong[p]=cnt;
instack[p]=false;
size--;
}
} void build(int l,int r,int rt)
{
if (l==r)
{
add_edge(rt+(n<<),z[l].p<=n?z[l].p+n:z[l].p-n);
return;
}
int m=(l+r)>>;
build(lson);
build(rson);
add_edge(rt+(n<<),(rt<<)+(n<<));
add_edge(rt+(n<<),(rt<<|)+(n<<));
} void insert(int l,int r,int rt,int nowl,int nowr,int p)
{
if (nowl<=l && r<=nowr)
{
add_edge(p,rt+(n<<));
return;
}
int m=(l+r)>>;
if (nowl<=m) insert(lson,nowl,nowr,p);
if (m<nowr) insert(rson,nowl,nowr,p);
} bool check(int k)
{
en=;cnt=;
memset(v,,sizeof(v));
memset(dfn,,sizeof(dfn)); build(wmt); int r=,l=; for (int a=;a<=(n<<);a++)
{
int op,p=z[a].p;
if (p<=n) op=pos[p+n];
else op=pos[p-n];
while (r<=a && z[r].v <= z[a].v-k)
r++;
if (r<a && r>= && z[r].v > z[a].v-k)
{
if (op>=r && op<=a-)
{
if (op>r) insert(wmt,r,op-,z[a].p);
if (op<a-) insert(wmt,op+,a-,z[a].p);
}
else insert(wmt,r,a-,z[a].p);
}
while (l<=(n<<) && z[l].v < z[a].v+k)
l++;
l--;
if (l>a && l<=(n<<) && z[l].v < z[a].v+k)
{
if (op>=a+ && op<=l)
{
if (op>a+) insert(wmt,a+,op-,z[a].p);
if (op<l) insert(wmt,op+,l,z[a].p);
}
else insert(wmt,a+,l,z[a].p);
}
} for (int a=;a<=(n<<);a++)
if (!dfn[a]) dfs(a);
for (int a=;a<=n;a++)
if (belong[a]==belong[a+n]) return false;
return true;
} int main()
{
readint(n);
int minv=0x3f3f3f3f,maxv=-0x3f3f3f3f;
int x=;
for (int a=;a<=n;a++)
{
int v1,v2;
readint(v1);
readint(v2);
z[++x]=rec(v1,a);
z[++x]=rec(v2,a+n);
minv=min(minv,min(v1,v2));
maxv=max(maxv,max(v1,v2));
}
if (maxv-minv+ < n)
{
printf("0\n");
return ;
}
sort(z+,z+x+);
for (int a=;a<=(n<<);a++)
pos[z[a].p]=a;
int l=,r=;
while (l+!=r)
{
int m=(l+r)>>;
if (check(m)) l=m;
else r=m;
}
printf("%d\n",l); return ;
}

f [ i ][ j ]:表示第 i 位到第 j 位所能组成的最长回文子序列;

考虑怎么使序列更长?我们分别在两端加一个相同的字符不就好了?

所以我们枚举 i 之前,j 之后找一个相同的字符就好了;

f [ i ][ j ] +1 -> f [ k ][ l ];

时间复杂度 O(n4

考虑到 dp 有三种写法:

1. 自己算别人;

2. 别人算自己;

3. 记忆化搜索;

我们刚刚的写法是自己去算别人,现在我们来考虑用别人算自己;

如果用第 i 个字符:f [ i+1 ][ j ];

如果用第 j 个字符:f [ i ][ j-1 ];

如果第 i 个字符和第 j 个字符我们都用:f [ i+1 ][ j-1 ];

那么转移就是:f [ i ][ j ] = max ( f [ i+1 ][ j ] , f [ i ][ j-1 ] , f [ i+1 ][ j-1 ] ) ,s [ i ] == s [ j ];

zhx:我们发现这道题十分的水,所以来加强一下,问本质不同的回文字符串有多少个?

对于本质相同:aba 类型算一种:121,252,707……;

f [ k+1 ][ l-1 ] 内的字符串都会被重新再算一次;

所以我们只需要再从答案里减去 f [ k+1 ][ l-1 ] 就行了;

k 和 l 的位置可以预处理;

整体复杂度 O(n2);

做排列类型的 dp 题时,我们考虑把数从小到大一个一个插进去;

排列的性质:

我们随便从排列里取出一个前缀,它还是个排列;

f [ i ][ j ]:我们已经用 1~i 将前 i 个位置填好了,第 i 个位置是 j 的方案数是多少;

转移:f [ i ][ j ] -> f [ i+1 ][ k ]

分两组情况:

1. ai > ai+1 ,也就是说我们接下来插的这个数比之前插的数要小:

那么我们能填的数就是 1 ~ j-1(前一个填的数是 j),但是显然我们要填的这个数必然在前面已经用过了,会发生冲突,怎么办呢?

我们把 1~i 中大于等于 k 的数全部 +1 就好:

假如我们第六位插入一个 3,发现小于前一个插入的 5,那么我们让前面所有大于等于 3 的数全部加一,这样就空出一个 3 来了:

2 3 4 1 5

2 4 5 1 6 3;

2. ai < ai+1

道理是一样的,我们所插入的数的范围就变成了:j < k <= i+1(要大于前面插入的那个数,同时要在 i+1 以内)

转移方程:

1. ai > ai+1 :f [ i+1 ][ k ] = f [ i ][ j ] (k < j)

2. ai < ai+1 :f [ i+1 ][ k ] = f [ i ][ j ] (j < k <= i+1 )

时间复杂度:O ( n3 );

考虑到加一的时候我们可以前缀和优化,所以就优化成了 O ( n2 );

日常盲设状态:f [ i ][ j ] 表示选了 i 个点到了第 j 层的方案数;

发现这个状态没有办法转移。。。那怎么办呢?

换一个状态:f [ i ][ j ] :表示已经用了 i 个点,它们的深度都小于等于 j 的方案数;

答案就是:f [ n ][ n ] - f [ n ][ k-1 ];

考虑转移:

我们考虑拆根,去枚举左右结点个数:

有两种可能的答案:

[ i,i,i ] ,[ i-1,i,i+2 ]

对于第二种情况,如果出现了超过 2 个的情况,我们完全可以将它们拆成第一种的情况,所以第二种的数量一定是不超过 2 个的;

状态:

f [ i ][ j ][ k ] 我们已经处理完了 1~ i 这些数,用了 j 个 i-1,i,i+1,k 个 i,i+1,i+2 的方案数,注意 j 和 k 一定是小于等于 2 的;

转移:

用 l 个 i+1,i+2,i+3;

然后咕咕咕了qwq~

f [ i ][ j ][ k ]:表示在 i 轮结束之后,aj < ak 的方案数,j < k;

Σ ( j >k ) f [ q ][ j ][ k ];

但是交换 j,k 两个位置不仅仅是影响这两个位置的值,还会对后面的位置产生影响;

时间复杂度 O(n2);

对拍讲解现场:

随机生成树:

扫把图:

zhx AK做题方法:

前 10~ 15 分钟全部用来读题,包含手推样例;

然后我们选择一个合理的做题的顺序,一般来说是 1,2,3 的顺序,但是也不一定嘛qwq;

先写暴力,拿到保底分;

然后朝着 100 分的方向前进;

最后 15 分钟一定要丢掉键盘,静坐待亡检查一下自己有没有zz(数组开大开小,文件输入输出……)!

10月清北学堂培训 Day 4的更多相关文章

  1. 10月清北学堂培训 Day 7

    今天是黄致焕老师的讲授~ 历年真题选讲 NOIP 2012 开车旅行 小 A 和小 B 决定外出旅行,他们将想去的城市从 1 到 n 编号,且编号较小的城市在编号较大的城市的西边.记城市 i 的海拔高 ...

  2. 10月清北学堂培训 Day 6

    今天是黄致焕老师的讲授~ T1 自信 AC 莫名 80 pts???我还是太菜了!! 对于每种颜色求出该颜色的四个边界,之后枚举边界构成的矩阵中每个元素,如果不等于该颜色就标记那种颜色不能最先使用. ...

  3. 10月清北学堂培训 Day 5

    今天是廖俊豪老师的讲授~ T1 第一次想出正解 30 pts: k <= 10,枚举如何把数放到矩阵中,O ( k ! ): 100 pts: 对于矩阵的每一列,我们二分最小差异值,然后贪心去判 ...

  4. 10月清北学堂培训 Day 3

    今天是钟皓曦老师的讲授~ zhx:题很简单,就是恶心一些qwq~ T1 别人只删去一个字符都能AC,我双哈希+并查集只有40?我太菜了啊qwq 考虑到越短的字符串越难压缩,越长的字符串越好压缩,所以我 ...

  5. 10月清北学堂培训 Day 2

    今天是杨溢鑫老师的讲授~ T1 物理题,不多说(其实是我物理不好qwq),注意考虑所有的情况,再就是公式要推对! #include<bits/stdc++.h> using namespa ...

  6. 10月清北学堂培训 Day 1

    今天是杨溢鑫老师的讲授~ T1 1 题意: n * m 的地图,有 4 种不同的地形(包括空地),6 种不同的指令,求从起点及初始的状态开始根据指令行动的结果. 2 思路:(虽然分了数据范围但是实际上 ...

  7. 7月清北学堂培训 Day 3

    今天是丁明朔老师的讲授~ 数据结构 绪论 下面是天天见的: 栈,队列: 堆: 并查集: 树状数组: 线段树: 平衡树: 下面是不常见的: 主席树: 树链剖分: 树套树: 下面是清北学堂课程表里的: S ...

  8. 8月清北学堂培训 Day6

    今天是杨思祺老师的讲授~ 图论 双连通分量 在无向图中,如果无论删去哪条边都不能使得 u 和 v 不联通, 则称 u 和 v 边双连通: 在无向图中,如果无论删去哪个点(非 u 和 v)都不能使得 u ...

  9. 8月清北学堂培训 Day2

    今天是赵和旭老师的讲授~ 背包 dp 模型 背包 dp 一般是给出一些“物品”,每个物品具有一些价值参数和花费参数,要求 在满足花费限制下最大化价值或者方案数. 最简单几种类型以及模型: 0/1背包: ...

随机推荐

  1. List、dictionary、hashtable、ArrayList集合

    集合的引用命名空间在 system.Collections下 1.为什么引入集合 因为数组长度是固定的,为了建立一个动态的"数组",所以引入了集合. 2.为什么引入ArrayLis ...

  2. win7 ReadyBoot 文件位置修改

    右键我的电脑,依次点开系统工具-性能-数据收集器集-系统-事件跟踪会话 在右边找到ReadyBoot,右键打开属性,会话框上方选择文件,根据示例文件名的路径找到ReadyBoot.etl文件,复制到你 ...

  3. django.http.request中QueryDict 对象

    在一个 HttpRequest 对象中, GET 和 POST 属性都是 django.http.QueryDict 的实例. QueryDict 是一个类似于字典的类,专门用来处理用一个键的多值.当 ...

  4. hibernate saveorupdate方法只有更新有效果,保存没有效果

    转自:https://blog.csdn.net/KAIXINLUOYE/article/details/72821014 单主键生成策略由native改成assigned后,问题解决.

  5. flask-sqlalchemy使用及数据迁移

    flask-sqlalchemy是flask框架在sqlalchemy基础上改造的一个orm框架 现在有两个实体Article文章和Category分类 一个分类下可能有多篇文章 相关示例: 项目相关 ...

  6. 【Mybatis】 入门

    一.概述 1.1 JDBC 1.2 JDBC编程中问题 1.3 MyBatis介绍 1.4 Mybatis架构 二.MyBatis入门程序 2.1 需求 2.2 引入MyBatis依赖 2.3 配置 ...

  7. 十分钟掌握Pandas(上)——来自官网API

    十分钟掌握Pandas(上)——来自官网API 其实不止10分钟,这么多,至少一天 一.numpy和pandas numpy是矩阵计算库,pandas是数据分析库,关于百度百科,有对pandas的介绍 ...

  8. js、jquery实现列表模糊搜索功能

    实现的搜索功能: 1. 可以匹配输入的字符串找出列表中匹配的项,列表框的高度跟随搜索出的列表项的多少改变 2. 可以点击某一项进行选中列表项 3. 可以按下上.下.回车键来控制列表项 4. 按下回车键 ...

  9. 分享一个自制的USB转HART模块

    HART协议是一种用于现场智能仪表和控制室设备之间的通讯协议.使用USB转HART模块可以很方便的对HART总线上的数据进行监控,并且可以远程控制.操作和校准HART设备.设计的模块主要采用的是USB ...

  10. JDK源码那些事儿之并发ConcurrentHashMap上篇

    前面已经说明了HashMap以及红黑树的一些基本知识,对JDK8的HashMap也有了一定的了解,本篇就开始看看并发包下的ConcurrentHashMap,说实话,还是比较复杂的,笔者在这里也不会过 ...