纪中DAY8做题小结

  • T1:少女觉
  • T2:灵知的太阳信仰
  • T3:多段线性函数
  • T4:DY引擎

T1:少女觉

Description
在幽暗的地灵殿中,居住着一位少女,名为古明地觉。
据说,从来没有人敢踏入过那座地灵殿,因为人们恐惧于觉一族拥有的能力——读心。
掌控人心者,可控天下。
咳咳。
人的记忆可以被描述为一个黑块(B)与白块(W)的序列,其中情感值被定义为序列中黑块数量与白块数量之比。
小五口在发动读心术时,首先要解析人的记忆序列,因此,需要将序列分割为一些段,并且要求每一段记忆序列的情感值都相等。
下面给出两个例子:
BWWWBB -> BW + WWBB (Ratio=1:1)
WWWBBBWWWWWWWWWB -> WWWB + BBWWWWWW + WWWB (Ratio=3:1)
现在小五手上有一个人的记忆序列,她想要知道,如何将手中的记忆序列分成尽可能多的段呢?

Input
第一行包含一个正整数T,代表数据组数。
对于每一组测试数据,第一行包含一个正整数N。
接下来N行描述一个序列,每行包含一个正整数K和一个大写字母C,表示序列接下来有连续K个颜色为C的方块。

Output
对于每组测试数据输出一行一个正整数,表示最多分成的段数。

Sample Input
3
3
1 B
3 W
2 B
4
3 W
3 B
9 W
1 B
2
2 W
3 W

Sample Output
2
3
5

Data Constraint
对于10%的数据,n<=15
对于20%的数据,n<=500
另有30%的数据,K=1
另有30%的数据,K<=50
对于100%的数据,N<=10 ^ 5,序列长度不超过10 ^ 9
保证对于全部测试点,输入文件行数不超过2.5*10 ^ 6

简要思路:本体其实是一道普通的贪心题,然而我在考试时愣是把问题复杂化,从而给自己挖了一个坑 。在本题中每一个被划分的字符串中B : W的比例相同,由公式a+cb+d=ab+cd\frac{a + c}{b+d} = \frac{a}{b} + \frac{c}{d}b+da+c​=ba​+dc​(多此一举了吧 )得出整个序列中B : W的比例与每个分序列的相同,并且序列能分就分,不会对后面的值造成影响。实现时首先要特判,如果只有一种字符那直接输出字符串的长度,然后,通过求最大公因数将B : W比例化到最简。接下来,从头到尾扫描一遍,用变量sumbsumbsumb,sumwsumwsumw记录已扫描到的b,w的数目。当扫描到B的区间时,设当前区间长为lenlenlen,用比例公式计算当前已扫描到的W的总数所对应的期望的B的总数,设为temp,若sumb&lt;temp&lt;sumb+lensumb &lt; temp &lt; sumb + lensumb<temp<sumb+len,则可从该区间中间或边缘分割,答案加1,B,W对调也成立。

#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
const int N = 1e5 + 5;
int t , n , sumb , sumw , totb , totw , num , ans;
int len[N] , pos[N];//pos中0代表b,1代表W
char ss;
inline int gcd( int x , int y ) {return y == 0 ? x : gcd( y , x % y );
}
int main () {//freopen( "silly.in" , "r" , stdin );//freopen( "silly.out" , "w" , stdout );scanf("%d",&t);while ( t-- ) {scanf("%d",&n);sumb = 0;sumw = 0;totb = 0;totw = 0;ans = 0;for ( int i = 1 ; i <= n ; ++i ) {scanf("%d",&num);len[i] = num;getchar();ss = getchar();switch(ss) {case 'W' : {totw += num;pos[i] = 1;break;}case 'B' : {totb += num;pos[i] = 0;break;}}}if ( !totb || !totw ) {printf("%d\n",max( totb , totw ));continue;}int d = gcd( totb , totw );totb /= d;totw /= d;int tem;for ( int i = 1 ; i <= n ; ++i ) {if ( !pos[i] ) {if ( sumw % totw == 0 ) {tem = ( (ll)sumw * totb ) / totw;//相乘可能会爆int,要开long longif ( tem > sumb && tem <= sumb + len[i] ) {ans++;}}sumb += len[i];} else {if ( sumb % totb == 0 ) {tem = ( (ll)sumb * totw ) / totb;//相乘可能会爆int,要开long longif ( tem > sumw && tem <= sumw + len[i] ) {ans++;}}sumw += len[i];}}printf("%d\n",ans);}return 0;
}

T2:灵知的太阳信仰

Description
在炽热的核熔炉中,居住着一位少女,名为灵乌路空。
据说,从来没有人敢踏入过那个熔炉,因为人们畏缩于空所持有的力量——核能。
核焰,可融真金。
咳咳。
每次核融的时候,空都会选取一些原子,排成一列。然后,她会将原子序列分成一些段,并将每段进行一次核融。
一个原子有两个属性:质子数和中子数。
每一段需要满足以下条件:
1、同种元素会发生相互排斥,因此,同一段中不能存在两个质子数相同的原子。
2、核融时,空需要对一段原子加以防护,防护罩的数值等于这段中最大的中子数。换句话说,如果这段原子的中子数最大为x,那么空需要付出x的代价建立防护罩。求核融整个原子序列的最小代价和。

Input
第一行一个正整数N,表示原子的个数。
接下来N行,每行两个正整数pi和ni,表示第i个原子的质子数和中子数。

Output
输出一行一个整数,表示最小代价和。

Sample Input
5
3 11
2 13
1 12
2 9
3 13

Sample Output
26

Data Constraint
对于20%的数据,1<=n<=100
对于40%的数据,1<=n<=1000
对于100%的数据,1<=n<=10 ^ 5,1<=pi<=n,1<=ni<=2*10 ^ 4

简要思路:本来呢,这题暴力是只能拿四十分的,但是,有一位大佬证明,有技巧的暴力是能过的(每次得到一个位置i向左,走到相同数为止),这有一个栗子。

(大佬的证明)假设 i 步就结算,那么花费了 i 步,概率是 i-1 步内未结束,在第 i 步时结束了。
P(i-1 步内未结束)=P(前 i-1 个互不相同)=(p/p)[(p-1)/p][(p-2)/p]……[(p-i)/p]=p!/[(p-i-1)!*p^(i-1)]
P(在第 i 步恰好结束)=P(i-1 步内未结束)*P(第 i 个与前 i-1 个中某一个相同)=p!/[(p-i-1)!p^(i-1)](i-1)/p
对期望的贡献为 P(在第 i 步恰好结束)*i
我们计算一下 10^5 时的期望,大约为 400+。
因此该暴力算法的期望复杂度确实能通过全部的数据。

不过,我们为了追求不被有心的出题人卡,应该想一个更优的方法(单调栈要登场了)。
根据题意,不难推出方程dp[i]=dp[j]+maxdp[i] = dp[j] + maxdp[i]=dp[j]+max{ j+1j + 1j+1 ~ iii };用数组maxnmaxnmaxn记录加号后面式子的值。
同时,lll数组记录每个数能向左拓展的最大范围,用单调队列维护数据,当headheadhead的位置在当前值最大范围以外,踢出;对于tailtailtail,踢出中子数不如当前中子数的,维护一个单调递增的单调栈。
处理完后,当前值就可以入单调队列了。
不过此时有两种情况:
如果队列中只有一个元素(刚加进来的),直接更新;
如果不止一个,由于最靠前的在当前值的活动范围之外,要特地更新一下(先出来再进去)。
还有疑问的看代码吧。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
using namespace std;
const int N = 1e5 + 5;
int numa[N] , numb[N] , l[N] , now[N] , sum[N] , maxn[N] , pos[N] , dp[N];
multiset <int> q;//它的作用是自动排序,并删除指定值,multiset可存值相同的元素
int n , head , tail;
inline void read( int & res ) {res = 0;int pd = 1;char a = getchar();while ( a < '0' || a > '9' ) {if ( a == '-' ) {pd = -pd;}a = getchar();}while ( a >= '0' && a <= '9' ) {res = ( res << 1 ) + ( res << 3 ) + ( a - '0' );a = getchar();}res *= pd;return;
}
inline int ma( int l , int r ) {int ans = -100;for ( int i = l ; i <= r ; ++i ) {ans = max( ans , numb[i] );}return ans;
}
int main () {//freopen( "array.in" , "r" , stdin );//freopen( "array.out" , "w" , stdout );read(n);for ( int i = 1 ; i <= n ; ++i ) {read(numa[i]);read(numb[i]);l[i] = now[numa[i]] + 1;//统计当前数的活动范围 now[numa[i]] = i;l[i] = max( l[i] , l[i - 1] );//左边数的范围也会对当前数的范围造成限制 }head = 1;tail = 0;for ( int i = 1 ; i <= n ; ++i ) {int k = i - 1;while ( head < tail && l[i] > pos[head + 1] ) {//踢出活动范围以外的 q.erase(q.find(sum[head]));//其实留了一个,对应后面改队头的操作head++;}while ( head <= tail && numb[i] > maxn[tail] ) {//维护单调栈 q.erase(q.find(sum[tail]));k = pos[tail]; tail--;}pos[++tail] = k;maxn[tail] = numb[i];if ( head != tail ) {sum[tail] = dp[pos[tail]] + maxn[tail];q.insert(sum[tail]);q.erase(q.find(sum[head]));}sum[head] = dp[l[i] - 1] + maxn[head];q.insert(sum[head]);dp[i] = *q.begin();}printf("%d",dp[n]);return 0;
}

T3:多段线性函数

Description

Input

Output
输出文件名为linear.out。
输出一行两个自然数,用空格隔开,依次为L和R。

Sample Input
5
1 3
2 3
3 5
5 5
6 7

Sample Output
3 5

Data Constraint

简要思路:本题有很多人用三分大法求解,本人表示看不出(然而,三分可行证明了函数是单极峰的),我用的方法十分投机,把所有数收在一起,排序,输出中间两个即可。
证明嘛,我不会,但也找不到反例,下面给出一个大佬的证明:

我们设取到的其中一个极值点在www,再设两个值 u=∑i=1n[li≤w]+[ri≤w]u = \sum_{i=1}^n [l_i \le w ] + [r_i \le w ]u=i=1∑n​[li​≤w]+[ri​≤w],u=∑i=1n[li&gt;w]+[ri&gt;w]u = \sum_{i=1}^n [l_i \gt w ] + [r_i \gt w ]u=i=1∑n​[li​>w]+[ri​>w],其中[x][x][x] 当xxx成立时值为1,否则为0。
我们显然要让∣u−v∣|u - v|∣u−v∣的值尽量小,函数值才会小。
所以我们把所有的左右边界丢在一起排个序,取中间两个就是答案了。(玩深沉提高严谨性)

//还有必要吗
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
int n , num[2 * N];
inline void read( int & res ) {res = 0;int pd = 1;char a = getchar();while ( a < '0' || a > '9' ) {if ( a == '-' ) {pd = -pd;}a = getchar();}while ( a >= '0' && a <= '9' ) {res = ( res << 1 ) + ( res << 3 ) + ( a - '0' );a = getchar();}res *= pd;return;
}
int main () {//freopen( "linear.in" , "r" , stdin );//freopen( "linear.out" , "w" , stdout );read(n);int l , r;for ( int i = 1 ; i <= n ; ++i ) {read(l);read(r);num[i * 2 - 1] = l;num[i * 2] = r;}sort( num + 1 , num + 1 + 2 * n );int nn = n * 2;printf("%d %d",num[nn / 2],num[nn / 2 + 1]);
}

T4:DY引擎

Description
BOSS送给小唐一辆车。小唐开着这辆车从PKU出发去ZJU上课了。
众所周知,天朝公路的收费站超多的。经过观察地图,小唐发现从PKU出发到ZJU的所有路径只会有N(2<=N<=300)个不同的中转点,其中有M(max(0, N-100) <=M<=N)个点是天朝的收费站。N个中转点标号为1…N,其中1代表PKU,N代表ZJU。中转点之间总共有E(E<=50,000)条双向边连接。
每个点还有一个附加属性,用0/1标记,0代表普通中转点,1代表收费站。当然,天朝的地图上面是不会直接告诉你第i个点是普通中转点还是收费站的。地图上有P(1<=P<=3,000)个提示,用[u, v, t]表示:[u, v]区间的所有中转点中,至少有t个收费站。数据保证由所有提示得到的每个点的属性是唯一的。
车既然是BOSS送的,自然非比寻常了。车子使用了世界上最先进的DaxiaYayamao引擎,简称DY引擎。DY引擎可以让车子从U瞬间转移到V,只要U和V的距离不超过L(1<=L<=1,000,000),并且U和V之间不能有收费站(小唐良民一枚,所以要是经过收费站就会停下来交完钱再走)。
DY引擎果然是好东西,但是可惜引擎最多只能用K(0<=K<=30)次。

Input
第一行有6个整数N,M,E,P,L,K分别代表:N个中转点,M个收费站,E条边,P个提示,DY引擎的有效距离L,DY引擎的使用次数K。
接下去E行,每行有3个整数u,v,w(1<=u, v<=N; 1<=w<=1,000,000)表示:u和v之间有一条长度为w的双向边。
接下去P行,每行有3个整数u,v,t(1<=u<=v<=N; 0<=t<=u-v+1)表示: [u, v] 标号区间至少有t个收费站。

Output
输出一个整数,表示小唐从PZU开到ZJU用的最短距离(瞬间转移距离当然是按0来计算的)。

Sample Input
6 2 6 2 5 1
1 2 1
2 3 2
3 6 3
1 4 1
4 5 2
5 6 3
2 5 2
4 6 2

Sample Output
1【样例解释】
4、5是收费站。1->2(1)->6(1)

Data Constraint
对于30%的数据保证:
2<=N<=30,max(0, N-10) <=M<=N,0<=k<=10
对于100%的数据保证:
2<=N<=300,max(0, N-100) <=M<=N,E<=50,000,1<=P<=3,000,1<=L<=1,000,000,0<=K<=30.

简要思路:一道没意思的套路码农题,差分约束系统加上SPFA即可。对于不会的差分约束系统的,我推荐这篇博客这篇博客(不是我的 )。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 305 ,  M = 1000005;
int n , m , ptot , p , l ,  k , head , tail;
int q[M] , d1[N] , map1[N][N] , d2[N][N] , map2[N][N] , vis[N] , t[N][N] , qq[M][2] , viss[N][N];
inline void read( int & res ) {res = 0;int pd = 1;char a = getchar();while ( a < '0' || a > '9' ) {if ( a == '-' ) {pd = -pd;}a = getchar();}while ( a >= '0' && a <= '9' ) {res = ( res << 1 ) + ( res << 3 ) + ( a - '0' );a = getchar();}res *= pd;return;
}
void doit() {d1[n] = ptot;d1[0] = 0;vis[n] = 1;head = tail = 1;q[tail] = n;while ( head <= tail ) {int cur = q[head];head++;for ( int i = 1 ; i <= n ; ++i ) {if ( i != cur ) {if ( d1[cur] + map1[cur][i] < d1[i] ) {d1[i] = d1[cur] + map1[cur][i];if ( !vis[i] ) {q[++tail] = i;vis[i] = 1;}}}}vis[cur] = 0;}
}
void floyd() {for ( int k = 1 ; k <= n ; ++k ) {if ( d1[k] - d1[k - 1] <= 0 ) {for ( int i = 1 ; i <= n ; ++i ) {for ( int j = 1 ; j <= n ; ++j ) {if ( i != j && i != k && j != k ) {t[i][j] = min( t[i][j] , t[i][k] + t[k][j] );}}}}}return;
}
void spfa() {head = tail = 1;qq[tail][0] = 1;qq[tail][1] = 0;viss[1][0] = 1;d2[1][0] = 0;while ( head <= tail ) {int cur = qq[head][0];int tim = qq[head][1];head++;for ( int i = 2 ; i <= n ; ++i ) {if ( i != cur ) {if ( d2[cur][tim] + map2[cur][i] < d2[i][tim] ) {d2[i][tim] = d2[cur][tim] + map2[cur][i];if ( !viss[i][tim] ) {qq[++tail][0] = i;qq[tail][1] = tim;viss[i][tim] = 1;}}if ( tim < k && t[cur][i] <= l && d2[i][tim + 1]  > d2[cur][tim] ) {d2[i][tim + 1] = d2[cur][tim];if ( !viss[i][tim + 1] ) {qq[++tail][0] = i;qq[tail][1] = tim + 1;viss[i][tim + 1] = 1;}}}}viss[cur][tim] = 0;}
}
int main () {//freopen( "dy4.in" , "r" , stdin );read(n);read(ptot);read(m);read(p);read(l);read(k);int u , v , tt;memset( map1 , 0x3f , sizeof(map1) );memset( map2 , 0x3f , sizeof(map2) );memset( t , 0x3f , sizeof(t) );memset( d1 , 0x3f , sizeof(d1) );memset( d2 , 0x3f , sizeof(d2) );for ( int i = 1 ; i <= m ; ++i ) {read(u);read(v);read(tt);map2[u][v] = map2[v][u] = min( map2[u][v] , tt );t[u][v] = t[v][u] = map2[u][v];}for ( int i = 1 ; i <= p ; ++i ) {read(u);read(v);read(tt);if ( map1[v][u - 1] > -tt ) {map1[v][u - 1] = -tt;}}for ( int i = 1 ; i <= n ; ++i ) {map2[i][i] = 0;map1[i - 1][i] = 1;map1[i][i - 1] = 0;}doit();//差分约束floyd();//预处理开挂路径长spfa();//跑路int ans = 100000005;for ( int i = 0 ; i <= k ; ++i ) {ans = min( ans , d2[n][i] );}printf("%d\n",ans);return 0;
}

纪中DAY8做题小结相关推荐

  1. 纪中DAY15做题小结

    纪中DAY15做题小结 T1:淘汰赛制 T2:方程的解 T3:物流运输 T4:矩阵乘法(mat) T1:淘汰赛制 Description 淘汰赛制是一种极其残酷的比赛制度.2 ^ n名选手分别标号1, ...

  2. 纪中DAY5做题小结

    纪中DAY5做题小结 T1:直角三角形 T2:排序 T3:自行车赛 T4:小L的数列 T1:直角三角形 Description 二维平面坐标系中有N个点. 从N个点选择3个点,问有多少选法使得这3个点 ...

  3. 纪中DAY10做题小结

    纪中DAY10做题小结 T1:库特的向量(code) T2:恭介的法则(rule) T3:沙耶的玩偶(doll) T1:库特的向量(code) Description 从前在一个美好的校园里,有一只( ...

  4. 纪中国庆10.5做题小结

    纪中国庆10.5做题小结 T1:教主的花园 T2:教主泡嫦娥 T3:保镖排队 T4:教主的别墅 T1:教主的花园 Description [问题背景] LHX教主最近总困扰于前来膜拜他的人太多了,所以 ...

  5. ACM本周搜索做题小结和心得体会

    这周除了接着看上次没看完剩下的题,主要的就是做题了 我把这周新学到的和以前还没总结过的以及做题心得再主要说一下 这周做题和看到的主要是以下这几类 我把这周做题时遇到的某几类题简单归了一下类然后总结 遍 ...

  6. USACO 做题小结

    还记得之前,发过一篇阶段性总结与未来规划..结果由于最近rp爆发(保研成功+进wf)后者显然靠bin神,前者也是运气.因此,放松了一段时间.然后就开始刷usaco了,原因是不用花时间找解题报告在NOC ...

  7. BUUCTF做题小结

    大帝的密码武器 题目:公元前一百年,在罗马出生了一位对世界影响巨大的人物,他生前是罗马三巨头之一.他率先使用了一种简单的加密函,因此这种加密方法以他的名字命名. 以下密文被解开后可以获得一个有意义的单 ...

  8. LeetCode 5661. 替换隐藏数字得到的最晚时间 做题小结

    题目 给你一个字符串 time ,格式为 hh:mm(小时:分钟),其中某几位数字被隐藏(用 ? 表示).有效的时间为 00:00 到 23:59 之间的所有时间,包括 00:00 和 23:59 . ...

  9. LeetCode 435. 无重叠区间 做题小结

    题目 给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠.注意:可以认为区间的终点总是大于它的起点. 区间 [1,2] 和 [2,3] 的边界相互"接触",但没有相 ...

最新文章

  1. 小微贷是美团的上坡之路?
  2. 分析模式:可复用的对象模型学习笔记
  3. 课外扩展:使用RADIUS来集中管理***服务器(2)
  4. JQuery判断数组中是否包含某个元素$.inArray(js, arr);
  5. [原]Jenkins(二十) jenkins再出发之Error: Opening Robot Framework log failed
  6. c# params 工作原理。
  7. java怎编写么解析一个类型_DAY3:你必须知道的java虚拟机之类篇——类文件的结构...
  8. Python函数调用(2)
  9. L1-050 倒数第N个字符串-PAT团体程序设计天梯赛GPLT
  10. Android开发BroadcastReceiver广播的使用
  11. 关于RH850系统时钟初始化的分析解读
  12. 【python】实现canny算子与LoG算子
  13. 《人月神话》阅读心得
  14. 读书笔记《区块链原理与技术 郑子彬》——区块链共识层
  15. css如何设置背景颜色透明?css设置背景颜色透明度的两种方法介绍
  16. html5 replace,js replace函数用法详解
  17. mac安装配置zsh
  18. AS+kotlin+SurfaceView最佳实践之打造六子棋小游戏
  19. 如何在HTML做个按钮
  20. 数据分析笔记--matplotlib(五)绘制条形图

热门文章

  1. 删除流氓软件 Alibaba PC Safe Service
  2. vue实现头部吸顶描点
  3. hostapd源码编译与配置
  4. 15个“在线配色方案”工具网站
  5. matlab二维绘图部分
  6. 三菱不同型号plc之间能否实现无线通讯?
  7. 基于Android Q电池服务分析
  8. 学会使用QT的帮助文档
  9. 8.17vue项目搜素框的实现
  10. 有哪些权威的职业性格测试?职业性格测试靠谱吗?