第一天
DeepDarkFantasy
从东京出发,不久便到一处驿站,写道:日暮里。 ——鲁迅《藤野先生》
定义一个置换的平方为对1~n的序列做两次该置换得到的序列。已知一个置换的平方,并且这个结果是一个排列,求该置换。
输入第一行一个数n表示排列长度,接下来一行n个数描述排列。
有解则输出一行n个数表示原排列。否则输出一行一个-1。
测试点编号 |
特征 |
0~1 |
n<=10 |
2~9 |
n<=1000000 |
此题有spj。
考试的时候懵逼了,根本没想清就开始乱打。
题解:由题易得每一个置换相当于一个n个点n条边的图,那么相当于是已知每个点在图上走两步的终点,求这个图。于是我们可以很快的得到,无论图中的环是奇环还是偶环,最终得到的答案都会是偶环。然后对于两种不同的情况分类讨论一下就可以得到解法了。
代码写丑了,虽然是O(N)的,但因为常数问题没能过。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=;
int a[maxn],num[maxn];
int ans[maxn],p[maxn];
int b[maxn],n;
int st[maxn],top;
int vis[maxn],c[maxn];
bool cmp(int x,int y){
return num[x]<num[y];
} int main(){
#ifndef ONLINE_JUDGE
freopen("deepdarkfantasy.in","r",stdin);
freopen("deepdarkfantasy.out","w",stdout);
#endif
scanf("%d",&n);
for(int i=,x;i<=n;i++){
scanf("%d",&x);
a[x]=i;p[i]=i;
}
int x,y,t,q,tot;
for(int i=;i<=n;i++){
if(a[i]==i){
b[i]=i;
continue;
}
if(!vis[i]){
x=i,tot=;
while(!vis[x]){
tot+=;
vis[x]=;
x=a[x];
}
st[++top]=x;
num[top]=tot;
c[tot]+=;
}
}
for(int i=;i<=n;i++)
if(i%==&&c[i]%){
printf("-1\n");
return ;
}
sort(p+,p+top+,cmp);
for(int i=;i<=top;i++){
x=p[i],t=st[x];
if(num[x]%){
for(int j=,k=;j<=num[x];j++){
ans[k]=t;
t=a[t];k+=;
if(k>num[x])k=;
}
ans[]=ans[num[x]];
for(int j=;j<=num[x];j++)
b[ans[j-]]=ans[j];
}
else{
y=p[++i],q=st[y];
for(int j=;j<=*num[x];j++){
if(j%){
ans[j]=t;
t=a[t];
}
else{
ans[j]=q;
q=a[q];
}
}
ans[]=ans[*num[x]];
for(int j=;j<=*num[x];j++)
b[ans[j-]]=ans[j];
}
}
for(int i=;i<=n;i++)
printf("%d ",b[i]);
printf("\n");
return ;
}
Wallace
“看来华莱士先生不太懂历史和哲♂学” ——自长者与美国记者华莱士的谈话
有两颗各含N个点的无根树,每棵树中点分别被编号为0,1,2,....,N-1;注意两棵树并不保证同构。另外给一个N长的整数数组Score[],记录N个编号的得分,Score中的每个元素可正可负。问题的任务是寻找 集合{0,1,2,3,4,...,N-1}的一个最优子集subset(可以是空集),要求满足以下条件:
1)在第一棵树中,subset中包含的编号对应的点能构成一个连通的子图;即去掉这棵树中所有subset中不包含的点后,剩下的点依然是一棵连通的树。
2)在第二棵树中,subset中包含的编号对应的点也能构成一个连通的子图;
3)使subset包含编号的总得分尽可能的大;即SUM{ Score[i] | i∈subset }能取到尽可能大的值。
输入第一行一个正整数N。第二行N个整数Score[]代表0~N-1每个点的权值。
后面N-1行每行两个整数u,v代表第一棵树里面u点和v点之间有连边。
再后面N-1行每行两个整数u,v代表第二棵树里面u点和v点之间有连边。
输出一行一个整数代表这个subset包含编号的总分的最大值。
测试点编号 |
特征 |
0~5 |
n<=15 |
6~9 |
两棵树形状与编号完全相同 |
12~24 |
2<=n<=50且|Score[i]|<=1000且0<=u,v<n |
这道题并不是很难,枚举一个点必选,则可以用网络流做。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std; const int maxn=;
const int maxm=;
const int INF=;
int cnt,tot,fir[maxn],fron[maxn],dis[maxn];
int to[maxm],nxt[maxm],gap[maxn],path[maxn];
int cap[maxm];queue<int>q; struct Max_Flow{
void Init(int tot_=){
tot=tot_;cnt=;
memset(fir,,sizeof(fir));
memset(dis,,sizeof(dis));
memset(gap,,sizeof(gap));
} void add(int a,int b,int c){
nxt[++cnt]=fir[a];
fir[a]=cnt;
cap[cnt]=c;
to[cnt]=b;
} void addedge(int a,int b,int c){
add(a,b,c);
add(b,a,);
} bool BFS(int s,int t){
dis[t]=;q.push(t);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=fir[x];i;i=nxt[i])
if(!dis[to[i]]){
dis[to[i]]=dis[x]+;
q.push(to[i]);
}
}
return dis[s];
} int Aug(int s,int t,int &p){
int f=INF;
while(p!=s){
f=min(f,cap[path[p]]);
p=to[path[p]^];
}p=t;
while(p!=s){
cap[path[p]]-=f;
cap[path[p]^]+=f;
p=to[path[p]^];
}
return f;
} int ISAP(int s,int t){
if(!BFS(s,t))return ;
for(int i=s;i<=t;i++)fron[i]=fir[i];
for(int i=s;i<=t;i++)gap[dis[i]]+=;
int p=s,ret=;
while(dis[s]<=tot){
if(p==t)ret+=Aug(s,t,p); for(int &i=fron[p];i;i=nxt[i])
if(cap[i]&&dis[p]==dis[to[i]]+){
path[p=to[i]]=i;
break;
} if(!fron[p]){
if(--gap[dis[p]]==)
break;
int Min=tot;
for(int i=fir[p];i;i=nxt[i])
if(cap[i])Min=min(Min,dis[to[i]]);
gap[dis[p]=Min+]+=;fron[p]=fir[p];
if(p!=s)p=to[path[p]^];
}
}
return ret;
}
}isap; int w[maxn],n;
int G[maxn][maxn];
int G2[maxn][maxn];
int dep[maxn],pre[maxn]; void Build(int x,int y){
int py=y;
while(x!=py){
if(dep[x]>dep[py]){
x=pre[x];
isap.addedge(y,x,INF);
}
else{
py=pre[py];
isap.addedge(y,py,INF);
}
}
} int DFS(int x,int fa){
int ret=;
if(w[x]>=){
isap.addedge(,x,w[x]);
ret+=w[x];
}
else isap.addedge(x,n+,-w[x]);
for(int i=;i<=n;i++)
if(G[x][i]&&i!=fa){
isap.addedge(i,x,INF);
Build(x,i);ret+=DFS(i,x);
}
return ret;
} void Prepare(int x,int fa){
for(int i=;i<=n;i++)
if(G2[x][i]&&i!=fa){
dep[i]=dep[x]+;
pre[i]=x;Prepare(i,x);
}
} int main(){
freopen("wallace.in","r",stdin);
freopen("wallace.out","w",stdout);
scanf("%d",&n);
for(int i=;i<=n;i++)
scanf("%d",&w[i]);
for(int i=,a,b;i<n;i++){
scanf("%d%d",&a,&b);
G[a+][b+]=G[b+][a+]=;
}
for(int i=,a,b;i<n;i++){
scanf("%d%d",&a,&b);
G2[a+][b+]=G2[b+][a+]=;
}Prepare(,);
int ans=,tmp;
for(int i=;i<=n;i++){
isap.Init(n+);tmp=DFS(i,);
ans=max(ans,tmp-isap.ISAP(,n+));
}
printf("%d\n",ans);
return ;
}
第二天
永不落幕的 永不落幕的 zy
【背景】
人们的感情像洪水一样袭来,头晕目眩。 人们的感情像洪水一样袭来,头晕目眩。 人们的感情像洪水一样袭来,头晕目眩。 人们的感情像洪水一样袭来,头晕目眩。 人们的感情像洪水一样袭来,头晕目眩。 人们的感情像洪水一样袭来,头晕目眩。 人们的感情像洪水一样袭来,头晕目眩。 人们的感情像洪水一样袭来,头晕目眩。
【题目 描述】 描述】
zy 是一位拥有“共感”能力的少年,他总意无地知别人心情。 是一位拥有“共感”能力的少年,他总意无地知别人心情。 是一位拥有“共感”能力的少年,他总意无地知别人心情。 是一位拥有“共感”能力的少年,他总意无地知别人心情。 是一位拥有“共感”能力的少年,他总意无地知别人心情。 是一位拥有“共感”能力的少年,他总意无地知别人心情。 是一位拥有“共感”能力的少年,他总意无地知别人心情。 是一位拥有“共感”能力的少年,他总意无地知别人心情。 是一位拥有“共感”能力的少年,他总意无地知别人心情。 是一位拥有“共感”能力的少年,他总意无地知别人心情。 是一位拥有“共感”能力的少年,他总意无地知别人心情。 是一位拥有“共感”能力的少年,他总意无地知别人心情。 是一位拥有“共感”能力的少年,他总意无地知别人心情。 是一位拥有“共感”能力的少年,他总意无地知别人心情。 是一位拥有“共感”能力的少年,他总意无地知别人心情。 是一位拥有“共感”能力的少年,他总意无地知别人心情。 sm 是一位 是一位 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 有着“心墙”的少女,她感情无法传递出去。就像命运一般他们相遇了仿佛 zy (的 能力)就是为了 能力)就是为了 能力)就是为了 能力)就是为了 sm 而存在一样(事实上也确如此)。 而存在一样(事实上也确如此)。 而存在一样(事实上也确如此)。 而存在一样(事实上也确如此)。 而存在一样(事实上也确如此)。 而存在一样(事实上也确如此)。 而存在一样(事实上也确如此)。 而存在一样(事实上也确如此)。 而存在一样(事实上也确如此)。
sm 的心墙由许多迷茫组成,每个都希望从 的心墙由许多迷茫组成,每个都希望从 的心墙由许多迷茫组成,每个都希望从 的心墙由许多迷茫组成,每个都希望从 的心墙由许多迷茫组成,每个都希望从 的心墙由许多迷茫组成,每个都希望从 的心墙由许多迷茫组成,每个都希望从 的心墙由许多迷茫组成,每个都希望从 的心墙由许多迷茫组成,每个都希望从 的心墙由许多迷茫组成,每个都希望从 X轴上某一点去到另,而只有 轴上某一点去到另,而只有 轴上某一点去到另,而只有 轴上某一点去到另,而只有 轴上某一点去到另,而只有 轴上某一点去到另,而只有 轴上某一点去到另,而只有 zy 能 消除 sm 的迷茫。面对数量如此庞大, 的迷茫。面对数量如此庞大, 的迷茫。面对数量如此庞大, 的迷茫。面对数量如此庞大, 的迷茫。面对数量如此庞大, 的迷茫。面对数量如此庞大, 的迷茫。面对数量如此庞大, 的迷茫。面对数量如此庞大, zy 不曾想要放弃,但有些迷失了方向。 不曾想要放弃,但有些迷失了方向。 不曾想要放弃,但有些迷失了方向。 不曾想要放弃,但有些迷失了方向。 不曾想要放弃,但有些迷失了方向。 不曾想要放弃,但有些迷失了方向。 不曾想要放弃,但有些迷失了方向。 不曾想要放弃,但有些迷失了方向。
假设现在时间是 假设现在时间是 假设现在时间是 假设现在时间是 0,zy 在心墙的 在心墙的 在心墙的 1位置, 位置, zy 每单位时间的移动速度是 每单位时间的移动速度是 每单位时间的移动速度是 每单位时间的移动速度是 每单位时间的移动速度是 每单位时间的移动速度是 1。
若现在 若现在 zy 携带的迷茫想要去地点和心墙上剩下中在他右边个数大于等左 携带的迷茫想要去地点和心墙上剩下中在他右边个数大于等左 携带的迷茫想要去地点和心墙上剩下中在他右边个数大于等左 携带的迷茫想要去地点和心墙上剩下中在他右边个数大于等左 携带的迷茫想要去地点和心墙上剩下中在他右边个数大于等左 携带的迷茫想要去地点和心墙上剩下中在他右边个数大于等左 携带的迷茫想要去地点和心墙上剩下中在他右边个数大于等左 携带的迷茫想要去地点和心墙上剩下中在他右边个数大于等左 携带的迷茫想要去地点和心墙上剩下中在他右边个数大于等左 携带的迷茫想要去地点和心墙上剩下中在他右边个数大于等左 携带的迷茫想要去地点和心墙上剩下中在他右边个数大于等左 携带的迷茫想要去地点和心墙上剩下中在他右边个数大于等左 携带的迷茫想要去地点和心墙上剩下中在他右边个数大于等左 携带的迷茫想要去地点和心墙上剩下中在他右边个数大于等左 携带的迷茫想要去地点和心墙上剩下中在他右边个数大于等左 携带的迷茫想要去地点和心墙上剩下中在他右边个数大于等左 边,则 边,则 zy 会向右移动,反之左。若现在 会向右移动,反之左。若现在 会向右移动,反之左。若现在 会向右移动,反之左。若现在 会向右移动,反之左。若现在 会向右移动,反之左。若现在 会向右移动,反之左。若现在 会向右移动,反之左。若现在 sm 没有任何迷茫,则 没有任何迷茫,则 没有任何迷茫,则 没有任何迷茫,则 没有任何迷茫,则 zy 原地不动。现在 原地不动。现在 原地不动。现在 原地不动。现在 zy 想 知道,组成心墙的每个迷茫分别在什么时候消失。 知道,组成心墙的每个迷茫分别在什么时候消失。 知道,组成心墙的每个迷茫分别在什么时候消失。 知道,组成心墙的每个迷茫分别在什么时候消失。 知道,组成心墙的每个迷茫分别在什么时候消失。 知道,组成心墙的每个迷茫分别在什么时候消失。 知道,组成心墙的每个迷茫分别在什么时候消失。 知道,组成心墙的每个迷茫分别在什么时候消失。 知道,组成心墙的每个迷茫分别在什么时候消失。 知道,组成心墙的每个迷茫分别在什么时候消失。
为了尽快地让 为了尽快地让 为了尽快地让 zy 能拥抱 能拥抱 sm ,请你帮他。 ,请你帮他。 ,请你帮他。 ,请你帮他。
【输入描述】 【输入描述】
第一行两个数 第一行两个数 第一行两个数 N,M表示迷茫的数量和心墙大小。接下来 表示迷茫的数量和心墙大小。接下来 表示迷茫的数量和心墙大小。接下来 表示迷茫的数量和心墙大小。接下来 表示迷茫的数量和心墙大小。接下来 表示迷茫的数量和心墙大小。接下来 表示迷茫的数量和心墙大小。接下来 表示迷茫的数量和心墙大小。接下来 N行 ,每行 ,每行 ,每3个整 数, t, a,b,表示这个迷茫出现在时刻 ,表示这个迷茫出现在时刻 ,表示这个迷茫出现在时刻 ,表示这个迷茫出现在时刻 ,表示这个迷茫出现在时刻 ,表示这个迷茫出现在时刻 ,表示这个迷茫出现在时刻 t,希望从 ,希望从 a到 b。
【输出描述】 【输出描述】
N行 N个整数,表示每迷茫分别在什么时候消失 个整数,表示每迷茫分别在什么时候消失 个整数,表示每迷茫分别在什么时候消失 个整数,表示每迷茫分别在什么时候消失 个整数,表示每迷茫分别在什么时候消失 个整数,表示每迷茫分别在什么时候消失 个整数,表示每迷茫分别在什么时候消失 个整数,表示每迷茫分别在什么时候消失 个整数,表示每迷茫分别在什么时候消失 个整数,表示每迷茫分别在什么时候消失
【样例输入】 【样例输入】
2 102 102 102 10
1 2 51 2 51 2 51 2 5
7 4 57 4 57 4 57 4 5
【样例输出】 【样例输出】
5
9
【样例解释】 【样例解释】
时刻 1,有迷茫出现了! ,有迷茫出现了! ,有迷茫出现了! ,有迷茫出现了! ,有迷茫出现了!
时刻 2,zy 移动到了位置 移动到了位置 移动到了位置 移动到了位置 2。
时刻 5,zy 移动到了位置 移动到了位置 移动到了位置 移动到了位置 5,迷茫消失。 ,迷茫消失。 ,迷茫消失。
时刻 7,有迷茫出现了! ,有迷茫出现了! ,有迷茫出现了! ,有迷茫出现了! ,有迷茫出现了!
时刻 8,zy 移动到了位置 移动到了位置 移动到了位置 移动到了位置 4。
时刻 9,zy 移动到了位置 移动到了位置 移动到了位置 移动到了位置 5,迷茫消失。 ,迷茫消失。 ,迷茫消失。
【数据范围】 【数据范围】
对于 30%30%30%的数据 的数据 : N, M, t <= 100t <= 100 t <= 100t <= 100 t <= 100t <= 100
对于 60%60%60%的数据 的数据 : N, M, t <= N, M, t <= N, M, t <= N, M, t <= N, M, t <= N, M, t <= 1000010000100001000010000
对于 100%100%100%100%的数据 的数据 : N, M, t <= 100000 N, M, t <= 100000 N, M, t <= 100000N, M, t <= 100000N, M, t <= 100000 N, M, t <= 100000N, M, t <= 100000N, M, t <= 100000N, M, t <= 100000N, M, t <= 100000N,
这么长的题面,呵呵了。
发现如果每次处理一个迷惘,则只有O(N)次操作,考虑将每次的复杂度降为log2N。
这里用的treap。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
const int maxn=;
const long long INF=10000000000000000LL;
struct Node{
long long t,a,b;
int id,tp;
Node(long long t_=,long long a_=,long long b_=,int id_=,int tp_=){
t=t_;a=a_;b=b_;id=id_;tp=tp_;
}
}px[maxn],t[maxn]; bool cmp(Node x,Node y){
return x.t<y.t;
} int cnt;
int sz[maxn],rt;
int ch[maxn][];
void Push_up(int x){
sz[x]=sz[ch[x][]]+sz[ch[x][]]+;
} int Newnode(Node x){
sz[++cnt]=;t[cnt]=x;
ch[cnt][]=ch[cnt][]=;
return cnt;
} int Merge(int x,int y){
if(!x||!y)return x|y;
if(rand()%){
ch[x][]=Merge(ch[x][],y);
Push_up(x);return x;
}
else{
ch[y][]=Merge(x,ch[y][]);
Push_up(y);return y;
}
} int ta,tb;
void Split(int p,long long k){
if(p==)return;
if(k==sz[ch[p][]]+){
ta=ch[p][];tb=p;
ch[p][]=;Push_up(p);
return;
}
if(k<sz[ch[p][]]+){
Split(ch[p][],k);
ch[p][]=tb;tb=p;
Push_up(p);
}
else{
k-=sz[ch[p][]]+;
Split(ch[p][],k);
ch[p][]=ta;ta=p;
Push_up(p);
}
} int tx,ty,tz;
void Get_div(int l,int r){
Split(rt,r+);ty=ta;tz=tb;
Split(ty,l);tx=ta;ty=tb;
} void Get_mer(){
tx=Merge(tx,ty);
rt=Merge(tx,tz);
} int ID,POS;
void Get_Prev(long long k){
int p=rt,num=;
while(p){
if(t[p].a<=k){
ID=p;num+=sz[ch[p][]]+;
POS=num;p=ch[p][];
}
else p=ch[p][];
}
return;
} void Get_Succ(long long k){
int p=rt,num=;
while(p){
if(t[p].a<k){
num+=sz[ch[p][]]+;
p=ch[p][];
}
else{
ID=p;POS=num+sz[ch[p][]]+;
p=ch[p][];
}
}
return;
} //严格小于x的个数
long long Get_Min(long long x){
Get_Succ(x);
return POS-;
} void Insert(Node x){
Get_Prev(x.a);
Split(rt,POS+);
ta=Merge(ta,Newnode(x));
rt=Merge(ta,tb);
} void Delete(int p){
Get_div(p,p);
rt=Merge(tx,tz);
} int n,m;
long long nt=,np=;
int Get_Tow(){
long long Min=Get_Min(np);
long long Max=sz[rt]-Min;
return Min<=Max?:-;
} void Init(){
rt=Newnode(Node(,-,,,));
rt=Merge(rt,Newnode(Node(,INF,,,)));
} void Print(long long p){
if(!p)return;
Print(ch[p][]);
printf("%lld ",t[p].a);
Print(ch[p][]);
} void Debug(){
printf("~~~~~~~~~~~~~~~~~\n");
Print(rt);
printf("\n~~~~~~~~~~~~~~~~~\n");
} long long ans[maxn];
int main(){
freopen("sm.in","r",stdin);
freopen("sm.out","w",stdout);
Init();
scanf("%d%d",&n,&m);srand(n);
for(int i=;i<=n;i++){
long long t,a,b;
scanf("%lld%lld%lld",&t,&a,&b);
px[i]=Node(t,a,b,i,);
}
sort(px+,px+n+,cmp);
for(int i=;i<=n;i++){
while(sz[rt]>){
int tow=Get_Tow();
if(tow==){
Get_Succ(np);
if(nt+t[ID].a-np>=px[i].t){
np+=px[i].t-nt;
break;
}
nt+=t[ID].a-np;
np=t[ID].a;
Delete(POS);
if(t[ID].tp==)
Insert(Node(,t[ID].b,,t[ID].id,));
else
ans[t[ID].id]=nt;
}
else{
Get_Prev(np);
if(nt+np-t[ID].a>=px[i].t){
np-=px[i].t-nt;
break;
}
nt+=np-t[ID].a;
np=t[ID].a;
Delete(POS);
if(t[ID].tp==)
Insert(Node(,t[ID].b,,t[ID].id,));
else
ans[t[ID].id]=nt;
}
}
Insert(px[i]);
nt=px[i].t;
//Debug();
}
while(sz[rt]>){
int tow=Get_Tow();
if(tow==){
Get_Succ(np);
nt+=t[ID].a-np;
np=t[ID].a;
Delete(POS);
if(t[ID].tp==)
Insert(Node(,t[ID].b,,t[ID].id,));
else
ans[t[ID].id]=nt;
}
else{
Get_Prev(np);
nt+=np-t[ID].a;
np=t[ID].a;
Delete(POS);
if(t[ID].tp==)
Insert(Node(,t[ID].b,,t[ID].id,));
else
ans[t[ID].id]=nt;
}
}
for(int i=;i<=n;i++)
printf("%lld\n",ans[i]);
return ;
}
第三天
博士的选取器(Selector.cpp)
【题目描述】
浏览到好图好句的时候,为了避免被删帖、封图、和谐,博士会掏出他的选取器,将好图好句框住,然后Ctrl+C,Ctrl+V。博士从初一开始坚持到现在,已经有2GB了。在反复地与吧主斗争之后,博士总结出了经验。
1).对于一篇帖子,可以从上至下分成N个片段,每个片段不可分割,且只有序号相邻的片段才相邻。
2).每次框选只能框选连续一段片段[L,R],而且这连续一段片段的总字符量不能超过LIMIT,否则会出错。
3).对于每次框选与复制,耗时只与这连续一段片段中字符量最大的那一个片段有关,而且满足正比例函数关系。为了让题目简单,规定耗时等于字符量。
4).两次框选之间的时间间隔可以视为0。
正在博士总结完经验的时候,他瞅到了一篇掺杂大量好图好句的帖子。于是,一场新的战争爆发了…………战局紧张,博士想知道他的最小耗时是多少。
【输入】
第一行两个整数N和Limit。
接下来的N行,每行一个整数,代表第I个片段的字符量
【输出】
仅一行,为博士的最小耗时。
【样例】
Sample Input (Selector.in)
Sample Output (Selector.out)
8 17
2
2
2
8
1
8
2
1
12
{解释:222/818/21,2+8+2=12}
【数据范围】
40%的数据N<=1000
100%的数据N<=300000
这个代码被卡常,无力吐槽,比时限长了0.05S左右,TM还是稳定的。
别人再带一个单调栈复杂度的,线段树优化一下照样踩我程序,QAQ……
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=;
const long long INF=1000000000000000LL;
int a[maxn];
long long sum[maxn],dp[maxn];
long long tx[maxn*],ty[maxn*],tz[maxn*],tw[maxn*]; void Push_down(int x){
if(ty[x]==INF)return;
ty[x<<]=ty[x];
ty[x<<|]=ty[x];
tz[x<<]=tx[x<<]+ty[x];
tz[x<<|]=tx[x<<|]+ty[x];
ty[x]=INF;
} void Push_up(int x){
tx[x]=min(tx[x<<],tx[x<<|]);
tz[x]=min(tz[x<<],tz[x<<|]);
} void Update(int x,int l,int r,int a,int b,int m){
if(a>b)return;
if(l>=a&&r<=b){
ty[x]=m;
tz[x]=m+tx[x];
return;
}
Push_down(x);
if(l+r>>>=a)Update(x<<,l,l+r>>,a,b,m);
if(l+r>><b)Update(x<<|,(l+r>>)+,r,a,b,m);
Push_up(x);
} void Insert(int x,int l,int r,int g){
if(l==r){
tx[x]=dp[l];
ty[x]=INF;
return;
}Push_down(x);
if(l+r>>>=g)Insert(x<<,l,l+r>>,g);
else Insert(x<<|,(l+r>>)+,r,g);
Push_up(x);
} long long Query(int x,int l,int r,int a,int b){
if(a>b)return INF;
if(l>=a&&r<=b)return tz[x];
Push_down(x);
long long ret=INF;
int mid=(l+r)>>;
if(mid>=a)
ret=Query(x<<,l,mid,a,b);
if(mid<b)
ret=min(ret,Query(x<<|,mid+,r,a,b));
return ret;
} void Insert2(int x,int l,int r,int g){
if(l==r){
tw[x]=a[l];
return;
}
if(l+r>>>=g)Insert2(x<<,l,l+r>>,g);
else Insert2(x<<|,(l+r>>)+,r,g);
tw[x]=max(tw[x<<],tw[x<<|]);
} int Q(int x,int l,int r,long long g){
if(l==r)return l;
if(tw[x<<|]<g)
return Q(x<<,l,l+r>>,g);
else
return Q(x<<|,(l+r>>)+,r,g);
}
int n,lim;
int main(){
freopen("Selector.in","r",stdin);
freopen("Selector.out","w",stdout);
scanf("%d%d",&n,&lim);
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
sum[i]=sum[i-]+a[i];
}
int Max0=;
memset(tx,,sizeof(tx));
memset(dp,,sizeof(dp));
int p=;
for(int i=;i<=n;i++){
while(sum[i]-lim>sum[p])p+=;
if(p==){
Max0=max(Max0,a[i]);
dp[i]=Max0;p+=;
}
int q=Q(,,n,a[i]);
Insert2(,,n,i);
Update(,,n,q,i-,a[i]);
dp[i]=min(dp[i],Query(,,n,p,i-));
Insert(,,n,i);
if(p==&&sum[i]<=lim)p=;
}
printf("%lld\n",dp[n]);
return ;
}