这场比赛DP只有100分,T2的DP写挂了...
没事我还有T1特别稳的100分在...
论数据为何如此之水...
T1:
题目大意:
从前有个湖.湖边有n座城,顺时针方向编号1-n.每个城用ai描述,ai可正可负.从前有个小B,他有个初始血量为1的血槽。现在他将从某个城市出发,沿顺时针方向遍历这n个城市(不回到出发地).小B经过第i个城市的时候血量将会增加ai.若a5=-2那么经过5号城市的时候,小B的血量将会减少2个单位.
小B的血量一定是正数,否则小B不安全.请求出有多少个出发地使得小B安全地走过n个城市.
注:1<=n<=10^6.
对这道题其实很简单,很多数据结构都可以做.
但是要卡时,所以我一直在想O(n)的做法,害怕线段树被卡,ST表又不敢写细节太多而且好像不可做...
没事先写n^2暴力.
写完之后就去写T2了,之后再跑回来写T1发现就是个傻逼单调队列...
先把数列复制一份,我们可以发现答案就是枚举1~n每个点.
那么从点i开始满足条件就是a[i],a[i]+a[i+1],...,a[i]+a[i+1]+...+a[i+n-1]的最小值大于等于0...
那么就用一个前缀和数组A[n]保存前缀和,假设当前枚举到了i,那么记录一个num表示-a[1]-a[2]-a[3]-...-a[i-1].
那么就用一个单调队列可以O(1)完成从i开始的答案转移到从i+1开始的答案...
然后就好办了...
考场AC代码如下:
#include<bits/stdc++.h>
using namespace std;
#define ACF inline void
typedef long long LL;
const int N=1000000;
LL a[2*N+2],A[2*N+2],num;
int h,t,n,sum;
struct node{
LL num;
int x;
}q[N*3+3];
ACF into(){
scanf("%d",&n);
for (int i=1;i<=n;i++){
scanf("%lld",&a[i]);
A[i]=a[i]+A[i-1];
}
for (int i=1;i<=n;i++){
a[i+n]=a[i];
A[i+n]=a[i+n]+A[i+n-1];
}
}
void push(LL num,int x){
while (q[t].num>num&&t>=h) t--;
q[++t].num=num;q[t].x=x;
}
void pop(int x){
if (q[h].x==x) h++;
}
LL top(){
return q[h].num;
}
ACF work(){
h=1;t=0;
for (int i=1;i<=n;i++) push(A[i],i);
for (int i=1;i<=n;i++){
if (top()+num>=0LL) sum++;
num-=a[i];
pop(i);
push(A[i+n],i+n);
}
}
ACF outo(){
printf("%d\n",sum);
}
int main(){
//freopen("sum.in","r",stdin);
//freopen("sum.out","w",stdout);
into();
work();
outo();
return 0;
}
T2:
题目大意:给定一张有向无环图,找出A到B的路径长度恰好是K的倍数的路径。这些路径中,最短的是多长呢?
注:n为点的数量,k为题目中的K,m为边的数量,vi为第i条边的权值,则1<=n,k<=1000,1<=m<=20000,1<=vi<=100000.
我在考场上的做法是一个DP,但写挂了...
思路就是用f[i][j]表示从A到i的路径中,边权和mod k后为j的最短路径的长度/k.
emmm思路大概没什么问题,考场代码如下:
#include<bits/stdc++.h>
using namespace std;
#define ACF inline void
const int N=1000;
const int M=20000;
const int INF=1000000000;
struct side{
int y,next,v;
}e[M+5];
int lin[N+5],f[N+5][N+5],n,m,k,A,B,top,hh,hg,cnt[N+5],t;
bool use[N+1];
queue < int > q;
void ins(int X,int Y,int V){
e[++top].y=Y;e[top].v=V;
e[top].next=lin[X];
lin[X]=top;
}
ACF into(){
scanf("%d%d%d%d%d",&n,&m,&k,&A,&B);
int x,y,z;
for (int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
ins(x,y,z);
cnt[y]++;
}
}
void topsort(){
for (int i=1;i<=n;i++)
if (!cnt[i]) q.push(i);
while (!q.empty()){
for (int i=lin[q.front()];i;i=e[i].next){
t=q.front();
for (int j=0;j<k;j++){
hh=j+e[i].v;hg=f[t][j];
if (hh>=k) hg++,hh-=k;
f[e[i].y][hh]=min(f[e[i].y][hh],hg);
}
cnt[e[i].y]--;
if (cnt[e[i].y]) continue;
q.push(e[i].y);
}
q.pop();
}
}
ACF work(){
for (int i=0;i<N+5;i++)
for (int j=0;j<N+5;j++)
f[i][j]=INF;
f[A][0]=0;
topsort();
}
ACF outo(){
if (f[B][0]!=INF) printf("%d\n",k*f[B][0]);
else printf("-1\n");
}
int main(){
//freopen("shortk.in","r",stdin);
//freopen("shortk.out","w",stdout);
into();
work();
outo();
return 0;
}
之后发现各种错误,先是把某个初始化写错了,然后是把某个东西用减法代替了除法和取模,还把min给漏掉了,然后INF还开太大了...
所以,AC代码如下:
#include<bits/stdc++.h>
using namespace std;
#define ACF inline void
const int N=1000;
const int M=20000;
const int INF=1000000000;
struct side{
int y,next,v;
}e[M+5];
int lin[N+5],f[N+5][N+5],n,m,k,A,B,top,hh,hg,cnt[N+5],t;
bool use[N+1];
queue < int > q;
void ins(int X,int Y,int V){
e[++top].y=Y;e[top].v=V;
e[top].next=lin[X];
lin[X]=top;
}
ACF into(){
scanf("%d%d%d%d%d",&n,&m,&k,&A,&B);
int x,y,z;
for (int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
ins(x,y,z);
cnt[y]++;
}
}
void topsort(){
for (int i=1;i<=n;i++)
if (!cnt[i]) q.push(i);
while (!q.empty()){
for (int i=lin[q.front()];i;i=e[i].next){
t=q.front();
for (int j=0;j<k;j++){
hh=j+e[i].v;hg=f[t][j]+hh/k;hh=hh%k;
f[e[i].y][hh]=min(f[e[i].y][hh],hg);
}
cnt[e[i].y]--;
if (cnt[e[i].y]) continue;
q.push(e[i].y);
}
q.pop();
}
}
ACF work(){
for (int i=0;i<N+5;i++)
for (int j=0;j<N+5;j++)
f[i][j]=INF;
f[A][0]=0;
topsort();
}
ACF outo(){
if (f[B][0]!=INF) printf("%d\n",f[B][0]*k);
else printf("-1\n");
}
int main(){
//freopen("shortk.in","r",stdin);
//freopen("shortk.out","w",stdout);
into();
work();
outo();
return 0;
}
T3:
我竟然被一颗线段树搞死了...
题目大意:给定一个数列Ai,长度为N.Ai为[1,K]范围内的整数.
M次操作,分为2种:
1、修改操作.修改数列中的单个元素.
2、询问操作.求数列A最短子段(包含元素个数最少且元素连续),使得子段包含[1,K]中每一个整数.
注:1<=N,M<=100000,1<=K<=50.
不会不会~_~...暴力都不会~_~...
论这颗线段树该怎么维护???随便维护...
正解的意思就是用一个long long数存下数集的状态.
我们可以发现一点的是维护一个前缀状态(即表示的是1~i联合起来的状态)改变只有k次.
所以我们每个节点维护一个前缀k数组和后缀k数组,数组中的第i项存k改变的位置以及改变后的k的数值.
注:这个前后缀是从这个节点的左右端点开始的.
然后就可以合并了,修改也很简单.
只不过代码长一点很多...
查询的话我们需要多存一个信息mi,表示最短长度,然后一个节点的mi可以从左右儿子的mi和中间部分维护两个指针用O(k)时间求出.
具体看代码:
#include<bits/stdc++.h>
using namespace std;
#define ACF inline void
#define F first
#define S second
const int K=50,N=262144;
const int INF=1e9;
const int half=131072;
typedef long long LL;
int wan,k,n,q;
typedef pair< LL , int > P;
struct tree{
int len,ans;
P pref[K+1],suff[K+1];
tree(){ //初始化
ans=INF;len=0;
};
tree(int t,int v){ //线段树底层修改
len=1;
pref[0]=suff[0]=P(1LL<<v,t);
ans=INF;
}
}tr[N+1];
bool in(LL a,LL b){ //判断a是否包含在b中
return (a&b)==a;
}
void merge(tree &t,tree &l,tree &r){
int pref_len=0,suff_len=0;
for (int i=0;i<l.len;i++)
t.pref[pref_len++]=l.pref[i]; //用左端点更新前缀
for (int i=0;i<r.len;i++) //用右端点更新前缀
if (!pref_len||!in(r.pref[i].F,t.pref[pref_len-1].F)){ //若前面未包含
t.pref[pref_len]=r.pref[i];
if (pref_len>0) t.pref[pref_len].F|=t.pref[pref_len-1].F;
pref_len++;
}
suff_len=0; //同上
for (int i=0;i<r.len;i++)
t.suff[suff_len++]=r.suff[i];
for (int i=0;i<l.len;i++)
if (!suff_len||!in(l.suff[i].F,t.suff[suff_len-1].F)){
t.suff[suff_len]=l.suff[i];
if (suff_len>0) t.suff[suff_len].F|=t.suff[suff_len-1].F;
++suff_len;
}
assert(pref_len==suff_len); //用中间部分更新答案
t.len=pref_len;
t.ans=INF;
int pref_pos=0;
for (int suff_pos=l.len-1;suff_pos>=0;suff_pos--) {
while (pref_pos<r.len&&(l.suff[suff_pos].F|r.pref[pref_pos].F)!=(1LL<<k)-1)
++pref_pos;
if (pref_pos<r.len) {
LL curr_mask=l.suff[suff_pos].F|r.pref[pref_pos].F;
if (curr_mask^(1LL<<k)-1) continue;
t.ans=min(t.ans,r.pref[pref_pos].S-l.suff[suff_pos].S+1);
}
}
t.ans=min(t.ans,min(l.ans,r.ans)); //用左右儿子更新
}
void change(int k,int v){ //注:用zkw线段树写的
k+=half;
tr[k]=tree(k-half,v);
for (k>>=1;k;k>>=1)
merge(tr[k],tr[k<<1],tr[k<<1|1]);
}
int query(){
return tr[1].ans;
}
ACF into(){
scanf("%d%d%d",&n,&k,&q);
int v;
for (int i=0;i<n;i++){
scanf("%d",&v);
--v;
change(i,v);
}
}
ACF work(){
int t,x,v,ans;
for (int i=1;i<=q;i++){
scanf("%d",&t);
if (t==1){
scanf("%d%d",&x,&v);
change(--x,--v);
}else{
ans=query();
if (ans==INF) printf("-1\n");
else printf("%d\n",ans);
}
}
}
ACF outo(){
}
int main(){
//freopen("short.in","r",stdin);
//freopen("short.out","w",stdout);
into();
work();
outo();
return 0;
}