0 少女觉
在幽暗的地灵殿中,居住着一位少女,名为古明地觉。
据说,从来没有人敢踏入过那座地灵殿,因为人们恐惧于觉一族拥有的能力——读心。
掌控人心者,可控天下。
咳咳。
人的记忆可以被描述为一个黑块(B)与白块(W)的序列,其中情感值被定义为序列中黑块数量与白块数量之比。
小五口在发动读心术时,首先要解析人的记忆序列,因此,需要将序列分割为一些段,并且要求每一段记忆序列的情感值都相等。
下面给出两个例子:
BWWWBB -> BW + WWBB (Ratio=1:1)
WWWBBBWWWWWWWWWB -> WWWB + BBWWWWWW + WWWB (Ratio=3:1)
现在小五手上有一个人的记忆序列,她想要知道,如何将手中的记忆序列分成尽可能多的段呢?
对于10%的数据,n<=15
对于20%的数据,n<=500
另有30%的数据,K=1
另有30%的数据,K<=50
对于100%的数据,N<=10^5 , 序列长度不超过10^9
保证对于全部测试点,输入文件行数不超过2.5*10^6
可以发现黑块与白块的比是确定的,是所有黑块比上所有白块,因而每一个小序列的比就是这个总比值
那么就按读入顺序往下做,每次一满足比值就马上记录答案
#include <cstdio>
#include <algorithm>
using namespace std;
int t,n,ans;
long long suma,sumb,wa,wb,p;
int a[100005],b[100005];
void read(int i){
char ch=getchar();
a[i]=0;
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9'){
a[i]=a[i]*10+ch-'0';
ch=getchar();
}
while (ch!='B'&&ch!='W') ch=getchar();
if (ch=='B') b[i]=1,sumb+=a[i]; else b[i]=0,suma+=a[i];
}
long long pk(long long s,long long a,long long b){
if ((a*s)%b==0) return (a*s)/b; else return -1;
}
int main(){
freopen("silly.in","r",stdin);
freopen("silly.out","w",stdout);
scanf("%d",&t);
for (;t;t--){
scanf("%d",&n);
suma=0,sumb=0,ans=0;
for (int i=1;i<=n;i++){
read(i);
}
if (suma==0||sumb==0){
printf("%d\n",max(suma,sumb));
continue;
}
wa=0;wb=0;
for (int i=1;i<=n;i++){
if (b[i]==0) {
long long p=pk(wb,suma,sumb);
if (p>wa&&p<=wa+a[i]) ans++;
wa+=a[i];
}
else
{
long long p=pk(wa,sumb,suma);
if (p>wb&&p<=wb+a[i]) ans++;
wb+=a[i];
}
}
printf("%d\n",ans);
}
}
1 灵知的太阳信仰
在炽热的核熔炉中,居住着一位少女,名为灵乌路空。
据说,从来没有人敢踏入过那个熔炉,因为人们畏缩于空所持有的力量——核能。
核焰,可融真金。
咳咳。
每次核融的时候,空都会选取一些原子,排成一列。然后,她会将原子序列分成一些段,并将每段进行一次核融。
一个原子有两个属性:质子数和中子数。
每一段需要满足以下条件:
1、同种元素会发生相互排斥,因此,同一段中不能存在两个质子数相同的原子。
2、核融时,空需要对一段原子加以防护,防护罩的数值等于这段中最大的中子数。换句话说,如果这段原子的中子数最大为x,那么空需要付出x的代价建立防护罩。求核融整个原子序列的最小代价和。
设 f[i] 为 i 与 i+1 之间分开,到i为止的代价和
预处理出 l[i] 表示 i 最早的相同质子数的位置
易得
f
[
i
]
=
m
i
n
(
f
[
j
]
+
b
[
j
+
1
—
—
i
]
)
,
l
[
i
]
<
j
<
=
i
f[i]=min(f[j]+b[j+1 ——i]),l[i]<j<=i
f[i]=min(f[j]+b[j+1——i]),l[i]<j<=i,这样的转移是
O
(
n
2
)
O(n^2)
O(n2)的
可以用单调队列维护合法的点组成的队列,每次更新f[i]只需循环合法的点,每次又都取出不合法的点
因为前一个点不能转移的点,后一个点也不能通过这个点转移来
(因为可能TLE所以稍微卡了个常?)
比如register , 快读 ,if 改为(xx条件xx?xx:xx)
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=100005;
const int inf=2000000000;
int n;
int l[N],p[N],w[N];
int f[N],e[N];
int read(){
char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
int x=0;
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x;
}
int main(){
freopen("array.in","r",stdin);
freopen("array.out","w",stdout);
n=read();
int ab;
for (register int i=1;i<=n;i++){
ab=read();w[i]=read();
l[i]=(l[i-1]>p[ab]?l[i-1]:p[ab]),
p[ab]=i,f[i]=inf;
}
int h=1,t=1;
f[1]=w[1],e[1]=1;
for (register int i=2;i<=n;i++){
while (h<=t&&e[h]<=l[i]) ++h;
while (h<=t&&w[i]>w[e[t]]) --t;
e[++t]=i;
for (int j=h+1;j<=t;j++)
f[i]=(f[i]>f[e[j-1]]+w[e[j]]?f[e[j-1]]+w[e[j]]:f[i]);
f[i]=(f[i]>f[l[i]]+w[e[h]]?f[l[i]]+w[e[h]]:f[i]);
}
printf("%d",f[n]);
}
2 多段线性函数
哎嗨,据dalao言此题是为阅读题
就,题目的函数公式,相当于y到每个区间的距离
因为要求的函数值尽可能小,所以当y在区间内xi肯定是取y ,这时对函数值无贡献;当y不在区间内xi也一定是取与y更近的端点
所以就只与端点有关
就把所有端点堆一起排个序,取中间两个就是答案
啊哈,中位数
#include <cstdio>
#include <algorithm>
using namespace std;
int n;
int a[200005];
int main(){
freopen("linear.in","r",stdin);
freopen("linear.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d%d",&a[i],&a[i+n]);
sort(a+1,a+1+n*2);
printf("%d %d",a[n],a[n+1]);
}
3 DY引擎
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)次。
这题可以拆分成两部分,找出收费站和找出最短路
其中找出收费站需要用到差分约束系统
差分约束系统
用于题目给出形如: a [ i ] − a [ j ] < = k a[i]-a[j]<=k a[i]−a[j]<=k 的约束条件,求 a [ i ] a[i] a[i]状态
首先我们荡开笔墨,先假设我们有一张有向图,已经求出了最短路,
a
[
i
]
a[i]
a[i]表示从1到i的最短路
现在有一条i到j的边,表示为
d
i
s
[
i
]
[
j
]
dis[i][j]
dis[i][j]
当然有
a
[
j
]
−
a
[
i
]
<
=
d
i
s
[
i
]
[
j
]
a[j]-a[i]<=dis[i][j]
a[j]−a[i]<=dis[i][j]
那么我们反过来,回到给出的约束条件
a
[
i
]
−
a
[
j
]
<
=
k
a[i]-a[j]<=k
a[i]−a[j]<=k,就相当于从j到i连一条权值为k的有向边
求出
a
[
i
]
a[i]
a[i]的状态当然也就是用最短路,快乐跑spfa
当然有很多题目不会直接给出约束条件,要自己从题目描述中找
再回到这道题
条件:[u, v]区间的所有中转点中,至少有t个收费站
设
s
[
i
]
s[i]
s[i]表示
1
1
1到
i
i
i的收费站总数,则条件转化为
s
[
v
]
−
s
[
u
−
1
]
>
=
t
s[v]-s[u-1]>=t
s[v]−s[u−1]>=t
再移下项,就是
s
[
u
−
1
]
−
s
[
v
]
<
=
t
s[u-1]-s[v]<=t
s[u−1]−s[v]<=t
这就相当于差分约束系统的条件了,求出
s
[
i
]
s[i]
s[i]状态后
s
[
i
]
−
s
[
i
−
1
]
s[i]-s[i-1]
s[i]−s[i−1]即为
i
i
i是否有收费站
然后就是用最短路跑DP啦
跑这个最短路呢,要记录第几个点,跳了几次
#include <cstdio>
#include <cstring>
using namespace std;
const int N=50002;
int n,m,e,p,l,k;
int ls[305],ne[N],y[N],w[N],bz[305],cnt;
int s[305],v[N*10],g[N*10],b[N][35],d[N*10];
int f[305][35],a[305][305];
void ad(int _u,int _v,int _w){
ne[++cnt]=ls[_u],ls[_u]=cnt,y[cnt]=_v,w[cnt]=_w;
}
void spfa1(){
memset(s,0x3f,sizeof s);
for (int i=1;i<=n;i++)
ad(i,i-1,0),ad(i-1,i,1);
int h=0,t=1;
v[1]=n;s[n]=m;bz[n]=1;
while (h<t){
int u=ls[v[++h]];
while (u!=-1){
if (s[v[h]]+w[u]<s[y[u]]){
s[y[u]]=s[v[h]]+w[u];
if (bz[y[u]]==0){
bz[y[u]]=1;
v[++t]=y[u];
}
}
u=ne[u];
}
bz[v[h]]=0;
}
for (int i=n;i>=1;i--) s[i]=s[i]-s[i-1];
}
void read(){
scanf("%d%d%d%d%d%d",&n,&m,&e,&p,&l,&k);
memset(a,0x3f,sizeof a);
for (int i=1;i<=e;i++){
int u,_v,_w;
scanf("%d%d%d",&u,&_v,&_w);
if (_w<a[u][_v]) a[u][_v]=a[_v][u]=_w;
}
for (int i=0;i<=n;i++) ls[i]=-1;
for (int i=1;i<=p;i++){
int _u,_v,_t;
scanf("%d%d%d",&_u,&_v,&_t);
ad(_v,_u-1,-_t);
}
}
void floyed(){
for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
if (i!=k)
for (int j=1;j<=n;j++)
if (i!=j&&j!=k&&s[k]==0&&a[i][k]!=a[0][0]&&a[k][j]!=a[0][0])
a[i][j]=(a[i][j]>a[i][k]+a[k][j]?a[i][k]+a[k][j]:a[i][j]);
}
void spfa2(){
cnt=0;
int h=0,t=1;
memset(b,0,sizeof b);
memset(f,0x3f,sizeof f);
b[1][0]=1,f[1][0]=0;
v[1]=1,d[1]=0;
while (h<t){
int x=v[++h],y=d[h];
for (int i=2;i<=n;i++)
if (a[x][i]!=a[0][0]){
if (f[x][y]+a[x][i]<f[i][y]){
f[i][y]=f[x][y]+a[x][i];
if (b[i][y]==0){
b[i][y]=1;
v[++t]=i;
d[t]=y;
}
}
if (y+1<=k&&a[x][i]<=l&&f[x][y]<f[i][y+1]){
f[i][y+1]=f[x][y];
if (b[i][y+1]==0){
b[i][y+1]=1;
v[++t]=i;
d[t]=y+1;
}
}
}
b[x][y]=0;
}
}
void print(){
int ans=1000000000;
for (int i=0;i<=k;i++)
ans=(ans>f[n][i]?f[n][i]:ans);
printf("%d",ans);
}
int main(){
read();
spfa1();
floyed();
spfa2();
print();
}