一天考两次****
T1
题面
有解情况下
枚举A数组中被删除的那个数的位置
在b[ i ]与b[ i+1 ]中插入的数为 k 的前提是b[ 1 ] 到b[ i ]中取遍1~k
但是会重复
比如序列1 1 2
在1前插入1和在2前插入1的效果是一样的
所以只保留最后可插入的位置
无解有两种情况
1 . b[ 1 ] 到b[ i ]取遍1~k,但b[ i+1 ]大于k+2
此时只有在b[ i+1 ]前插入k+1~b[i+1]-1才行
由于只能插入一个数
所以b[ i+1 ]最大为k+2
2 . b[ 1 ] 到b[ i ]取遍1~k,b[ i+1 ]等于k+2
此时必须在b[ i+1 ]前插入k+1才有解
但若此情况出现了两次及以上,并且每次需求插入的数不同
那么插入一个数就解决不了问题
代码
1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 int n; 5 long long flag,ans,maxx,b[1000010]; 6 int main() 7 { 8 freopen("A.in","r",stdin); 9 freopen("A.out","w",stdout); 10 scanf("%d",&n); 11 for(int i=1;i<n;i++) 12 { 13 scanf("%lld",&b[i]); 14 if(b[i]>maxx+2) 15 { 16 printf("0\n"); 17 return 0; 18 } 19 if(b[i]==maxx+2) 20 { 21 if(flag) 22 { 23 printf("0\n"); 24 return 0; 25 } 26 else flag=b[i]; 27 } 28 maxx=max(maxx,b[i]); 29 } 30 maxx=0; 31 for(int i=1;i<=n;i++) 32 { 33 if(flag&&maxx==flag-2) 34 { 35 ans++; 36 } 37 else if(!flag) 38 { 39 ans+=maxx+1; 40 if(b[i]>0&&maxx+1>=b[i]) ans--; 41 } 42 maxx=max(maxx,b[i]); 43 } 44 printf("%lld",ans); 45 return 0; 46 }
T2
题面
备用钥匙(B.c/cpp/pas/in/out) (Time Limit:1s Memory Limit:256MB) 【Description】 你知道 Just Odd Inventions 社吗?这个公司的业务是“只不过是奇妙的发明(Just Odd Inventions)”。这 里简称为 JOI 社。 JOI 社有 N 名员工,编号从 1 到 N。所有员工的工作时间从时刻 0 持续到时刻 M,时刻 0 和时刻 M 的时候,所有员工都必须在公司内。 某天,出于巧合,JOI 社的每个员工都要出行恰好一次。员工 i(1<=i<=N)在时刻 Si 离开公司,时刻 Ti 回到公司。同一时刻不会同时有两名以上的员工离开或回到公司。 JOI 社的入口处有一扇巨大的门,员工只能通过这扇门离开或回到公司。门上挂着一把锁,从公司内 部可以任意开锁或上锁,但从公司外部只有持有备用钥匙的人才能开锁或者上锁。时刻 0 时,锁是锁上的。 每个社员在回到公司的时候,都必须能够进入公司。换句话说,对于任意 1<=i<=N,要么员工 i 持有 备用钥匙,要么时刻 Ti 时门是开着的,否则是不被允许的。员工回到公司的时候,或者携带备用钥匙的员 工离开公司的时候,可以选择锁门或不锁。没有携带备用钥匙的员工离开公司的时候没有办法锁门。 JOI 社的社长决定把备用钥匙交给 N 个员工中的 K 个人。为了避免钥匙的丢失,员工之间不允许借用 钥匙。此外,JOI 社的社长很重视时间效率,因此每个员工在离开或回到公司的时刻以外,不允许开锁或 者上锁。 出于安全的考虑,社长希望上锁的时间越长越好。现在他将员工出入公司的信息和准备交给员工的钥 匙数量告诉了你,请你求出在能使所有员工回到公司的时候都能进入公司的大门的前提下,上锁的时间最 长是多少。 【Input】 第一行三个空格分隔的整数 N,M,K,表示 JOI 社的员工有 N 个,工作时间从时刻 0 到时刻 M,备用钥 匙有 K 把。 接下来 N 行,第 i 行(1<=i<=N)有两个空格分隔的整数 Si,Ti,表示员工 i 在时刻 Si 离开公司,时刻 Ti 回到公司。 【Output】 输出一行一个正整数,表示上锁时间总和的最大值。 【Sample Input】 4 20 2 3 11 5 15 6 10 12 18 【Sample Output】 13 【HINT】 JOI 社共有 4 名员工,工作时间为时刻 0~时刻 M,共有两把备用钥匙。 将钥匙交予员工 2 和员工 4,一天日程如下: 时刻 0,锁是关闭状态 时刻 3,员工 1 离开公司。由于员工 1 没有备用钥匙,无法锁门。 时刻 5,员工 2 离开公司,锁门。 时刻 6,员工 3 离开公司。由于员工 3 没有备用钥匙,无法锁门。 时刻 10,员工 3 回到公司,不锁门。 时刻 11,员工 1 回到公司,锁门。 时刻 12,员工 4 离开公司,锁门。 时刻 15,员工 2 回到公司,锁门。 时刻 18,员工 4 回到公司,锁门。 直到时刻 20 为止,锁都保持关闭状态。上锁的时间段为 0~3,5~6,11~20,总计 13 时段,故答案为 13。 【Data Constraint】 对于 20%的数据,1<=N<=20,1<=M<=10^6 对于 100%的数据: 1<=N<=2000 1<=M<=10^9 1<=K<N 0<Si<Ti<M (1<=i<=N) 对于任意 i,j (1<=i<=N,1<=j<=N,i≠j),Si≠Sj,Si≠Tj,Ti≠Tj
把所有进与出的时间点放在一起排序
相邻两个时间点有四种情况
第一种:左出右出
只需要左端点那个人带钥匙该区间便可取。
第二种:左出右进
1.若为同一人,则自己带钥匙该区间便可取。
2.若不是同一个人,两人都带钥匙该区间便可取。
但两人贡献重复,无法直接加在表示点的贡献的val数组里,连长 k 的边表示两者都选有 k 贡献
第三种:左进右出
都不用拿,直接计入答案。
第四种:左进右进
右端点那个人带钥匙该区间便可取。
连边后是形成了一堆连通块,并且每个连通块的形式都是一条链数组l
把链压平排列在一起形成一个序列,不同链首尾之间连0边
给定一个n个点序列,选取每个点有贡献,并且同时选取一个点以及它的上一个点有额外贡献,求选出k个点的最大贡献。
之后用dp
f[ i ][ j ][ 0 ]表示前 i 个点选了 j 个并且不选 i 的最高得分
f[ i ][ j ][ 0 ] = max ( f [ i-1 ][ j ][ 0 ],f [ i-1 ][ j ][ 1 ] )
f[ i ][ j ][ 1 ]表示前 i 个点选了 j 个并且选 i 的最高得分
if ( j == 1 ) f [ i ][ j ][ 1 ] = f [ i-1 ][ j-1 ][ 0 ] + val [ l [ i ] ]只选一个且选了第 i 个,那么前i-1个都不能选
if ( j >= 2 ) f [ i ][ j ][ 1 ] = max ( f [ i-1 ][ j-1 ][ 0 ],f [ i-1 ][ j-1 ][ 1 ]+dis [ i ])+val [ l [ i ] ]
代码
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 using namespace std; 5 int n,m,kk; 6 struct pot 7 { 8 int l,r; 9 }k[2020]; 10 struct node 11 { 12 int t,id; 13 bool p;//0in ,1out 14 node(int _t=0,int _id=0,bool _p=0):t(_t),id(_id),p(_p){} 15 }s[4010]; 16 bool cmp(node a,node b) 17 { 18 return a.t<b.t; 19 } 20 int val[2020],to[2020][3],v[2020][3];//0 ru,1 chu 21 void add(int a,int b,int c) 22 { 23 //printf("%d->%d==%d\n",a,b,c); 24 to[a][1]=b; 25 v[a][1]=c; 26 to[b][0]=a; 27 v[b][0]=c; 28 } 29 int l[2020],vis[2020]; 30 int dis[2020];//i-1->i的贡献 31 void dfs(int x) 32 { 33 vis[x]=1; 34 if(to[x][1]) 35 { 36 dfs(to[x][1]); 37 } 38 l[++l[0]]=x; 39 dis[l[0]]=v[x][1]; 40 } 41 int f[2020][2020][2],ans; 42 int main() 43 { 44 freopen("B.in","r",stdin); 45 freopen("B.out","w",stdout); 46 scanf("%d%d%d",&n,&m,&kk); 47 for(int i=1;i<=n;i++) 48 { 49 scanf("%d%d",&k[i].l,&k[i].r); 50 s[i*2-1]=node(k[i].l,i,1); 51 s[i*2]=node(k[i].r,i,0); 52 } 53 sort(s+1,s+1+n*2,cmp); 54 for(int i=1;i<n*2;i++) 55 { 56 int tmp=s[i+1].t-s[i].t; 57 if(s[i+1].p&&s[i].p)//左出右出,只需要左端点那个人带钥匙该区间便可取 58 { 59 val[s[i].id]+=tmp; 60 } 61 else if(s[i].p&&!s[i+1].p)//左出右进,两人都带钥匙该区间便可取,但两人贡献重复,连边表示加上tmp的贡献 62 { 63 if(s[i].id==s[i+1].id) val[s[i].id]+=tmp; 64 else add(s[i].id,s[i+1].id,tmp); 65 } 66 else if(!s[i].p&&!s[i+1].p)//左进右进,右端点那个人带钥匙该区间便可取 67 { 68 val[s[i+1].id]+=tmp; 69 } 70 else if(!s[i].p&&s[i+1].p) ans+=tmp; 71 //for(int i=1;i<=n;i++) printf("%d ",val[i]); 72 //printf("\n"); 73 } 74 for(int i=1;i<=n;i++) 75 { 76 if(!vis[i]&&!to[i][0])dfs(i); 77 } 78 //for(int i=1;i<=n;i++) printf("%d,%d\n",l[i],val[l[i]]); 79 for(int i=1;i<=n;i++) 80 { 81 for(int j=0;j<=kk;j++) 82 { 83 ////f[i][j][1]表示前 i个点选了 j个并且不选 i的最高得分 84 f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]); 85 //f[i][j][1]表示前 i个点选了 j个并且选 i的最高得分 86 if(j) f[i][j][1]=f[i-1][j-1][0]+val[l[i]]; 87 if(j>=2) f[i][j][1]=max(f[i-1][j-1][0],f[i-1][j-1][1]+dis[i])+val[l[i]]; 88 //printf("f[%d][%d]=%d\n",i,j,f[i][j][1]); 89 90 } 91 } 92 ans+=s[1].t; 93 ans+=m-s[n*2].t; 94 printf("%d",ans+max(f[n][kk][0],f[n][kk][1])); 95 return 0; 96 }
T3
题面
IOIOI卡片占卜(C.c/cpp/pas/in/out) (Time Limit:1s Memory Limit:256MB) 【Description】 K理事长很喜欢占卜,经常用各种各样的方式进行占卜。今天,他准备使用正面写着”I”,反面写着”O”的卡片为今年IOI的日本代表队占卜最终的成绩。 占卜的方法如下所示: 1.首先,选择5个正整数A,B,C,D,E。 2.将A+B+C+D+E张IOI卡片排成一行,最左侧的A张卡片正面朝上,接下来B张反面朝上,接下来C张卡片正面朝上,接下来D张反面朝上,最后E张正面朝上。如此排列的话,从左侧开始顺次为A张“I”,B张“O”,C张“I”,D张“O”,E张“I”。 3.在预先决定的N种操作中选出至少1种,然后按照任意顺序执行。(注:同种操作执行多次也是可以的。)这里,第i种操作(1<=i<=N)为【将从左数第Li张卡片到第Ri张卡片全部翻转】。翻转一张卡片需要1秒的时间,因此第i种操作耗时Ri-Li+1秒。 4.操作结束后,如果所有卡片都是正面朝上则占卜成功。 K理事长不想翻多余的牌,因此在实际使用卡片占卜之前会先计算出是否存在占卜成功的可能性。进一步,如果占卜可能成功,他会计算出能使占卜成功所消耗的时间的最小值。 现在给出卡片的排列信息和预先决定的操作信息,请你写一个程序,计算出占卜能否成功,如果能成功,输出消耗时间的最小值。 【Input】 第一行5个空格分隔的整数A,B,C,D,E,表示占卜初始时,从最左端开始依次是A枚正面朝上,接下来B枚背面朝上,接下来C枚正面朝上,接下来D枚背面朝上,最后E枚正面朝上。 接下来一行一个正整数N,表示预先决定的操作种类数。 接下来N行,第i行(1<=i<=N)两个空格分隔的正整数Li,Ri,表示第i种操作为【将从左数第Li张卡片到第Ri张卡片全部翻转】。 【Output】 如果占卜能够成功,输出消耗时间的最小值,否则输出-1。 【Sample Input】 1 2 3 4 5 3 2 3 2 6 4 10 【Sample Output】 12 【HINT】 初始的卡片序列为IOOIIIOOOOIIIII。 执行第2种操作后得到IIIOOOOOOOIIIII,耗时5秒。 接下来执行第3中操作,得到IIIIIIIIIIIIIII,占卜成功,耗时7秒。 耗时12秒完成占卜,这是耗时的最小值,故输出12。 【Data Constraint】 对于15%的数据,N<=10 对于另外50%的数据,1<=A,B,C,D,E<=50 对于100%的数据: 1<=A,B,C,D,E,N<=10^5 1<=Li<=Ri<=A+B+C+D+E (1<=i<=N)
每次由I变为O或者由O变为I时为1
把序列转换为一个有4个1的01串
每次对 l , r 的修改看成一条连接 l-1 , r 的权值为 r-l+1 的双向边。
以每个原始序列的1为起点跑一遍最短路,再枚举配对方案
1 #include<cstdio> 2 #include<iostream> 3 #include<queue> 4 #include<algorithm> 5 using namespace std; 6 int a,b,c,d,e,n,rr; 7 long long ans=2e18; 8 int head[500010],to[200010],nxt[200020],cnt; 9 long long val[200020]; 10 void add(int a,int b,long long c) 11 { 12 cnt++; 13 nxt[cnt]=head[a]; 14 head[a]=cnt; 15 to[cnt]=b; 16 val[cnt]=c; 17 } 18 struct p 19 { 20 int id; 21 long long dis; 22 p(int _id=0,long long _dis=0):id(_id),dis(_dis){} 23 friend bool operator <(p a,p b) 24 { 25 return a.dis>b.dis; 26 } 27 }; 28 long long dd[5][5],dis[500010]; 29 int vis[500010],l[5]; 30 priority_queue<p> q; 31 void dij(int s) 32 { 33 while(!q.empty()) q.pop(); 34 for(int i=0;i<=rr;i++) dis[i]=2e15,vis[i]=0; 35 dis[l[s]]=0; 36 q.push(p(l[s],0)); 37 while(!q.empty()) 38 { 39 int x=q.top().id; 40 q.pop(); 41 if(vis[x]) continue; 42 vis[x]=1; 43 for(int i=head[x];i;i=nxt[i]) 44 { 45 if(dis[to[i]]>dis[x]+val[i]) 46 { 47 dis[to[i]]=dis[x]+val[i]; 48 q.push(p(to[i],dis[to[i]])); 49 } 50 } 51 } 52 for(int i=1;i<=4;i++) 53 { 54 if(i==s) continue; 55 dd[s][i]=dis[l[i]]; 56 } 57 } 58 int main() 59 { 60 freopen("C.in","r",stdin); 61 freopen("C.out","w",stdout); 62 scanf("%d%d%d%d%d",&a,&b,&c,&d,&e); 63 rr=a+b+c+d+e; 64 scanf("%d",&n); 65 for(int i=1,r,ll;i<=n;i++) 66 { 67 scanf("%d%d",&ll,&r); 68 add(ll-1,r,r-ll+1); 69 add(r,ll-1,r-ll+1); 70 } 71 l[1]=a; 72 l[2]=l[1]+b; 73 l[3]=l[2]+c; 74 l[4]=l[3]+d; 75 for(int i=1;i<=4;i++) dij(i); 76 for(int i=1;i<=4;i++) 77 { 78 for(int j=1;j<=4;j++) 79 { 80 if(j==i) continue; 81 for(int k=1;k<=4;k++) 82 { 83 if(k==i||k==j) continue; 84 for(int o=1;o<=4;o++) 85 { 86 if(o==i||o==j||o==k) continue; 87 long long tmp=dd[i][j]+dd[k][o]; 88 ans=min(ans,tmp); 89 } 90 } 91 } 92 } 93 if(ans<2e15) printf("%lld",ans); 94 else printf("-1"); 95 return 0; 96 }