省选前的JOI

RT,发现找不到题,于是又开了新坑

JOI特色:重思考,代码难度(相比NOI系列)基本没有

(省选前到处挖坑2333)

 


 

JOI 2017 Final

焚风现象

差分,莫得了

(不是看到200ms就tm线段树去了

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=100005;
 6 long long ans,dif[N];
 7 int n,m,s,t,t1,t2,t3,rd,lst; 
 8 int main()
 9 {
10     scanf("%d%d%d%d",&n,&m,&s,&t);
11     scanf("%d",&rd);
12     for(int i=1;i<=n;i++)
13     {
14         scanf("%d",&rd);
15         dif[i]=rd-lst,lst=rd; 
16         ans-=(dif[i]>0)?(1ll*s*dif[i]):(1ll*t*dif[i]);
17     }
18     for(int i=1;i<=m;i++)
19     {
20         scanf("%d%d%d",&t1,&t2,&t3);
21         ans+=(dif[t1]>0)?(1ll*s*dif[t1]):(1ll*t*dif[t1]);
22         dif[t1]+=t3;
23         ans-=(dif[t1]>0)?(1ll*s*dif[t1]):(1ll*t*dif[t1]);
24         if(t2!=n)
25         {
26             t2++;
27             ans+=(dif[t2]>0)?(1ll*s*dif[t2]):(1ll*t*dif[t2]);
28             dif[t2]-=t3;
29             ans-=(dif[t2]>0)?(1ll*s*dif[t2]):(1ll*t*dif[t2]);
30         }
31         printf("%lld\n",ans);
32     }
33     return 0;
34 }
View Code

准高速电车

观察性质以进行贪心

题意可能有那么一点糊,其实是你可以走多次然后求和,只要每次都不超过T即可

注意题目保证速度按常识给出

我们发现一次出行一定是先坐快车,然后换乘准快车,最后坐慢车。而且显然我们最后一次一定是从一个快车站开始然后到不了下一个快车站的,所以我们只要考虑两两相邻的快车站中间即可

那我们就先只考虑一对相邻的快车站且我们在中间结束的情况,我们现在要在中间放一些准快车站,而我们选择在哪个准快车站结束相当于在后面一段用剩下的时间对右侧进行了一个覆盖,且这个覆盖的长度单调不升(显然越到后面剩的时间越少)。同样显然的是覆盖不会相交(否则不优)且一个覆盖的结束与下一个准快车站(如果还有的话)是相邻的(否则也不优)。于是问题变成了贪心,用堆维护每对相邻的快车站里准快车站覆盖的区间即可,记录剩余时间,剩余车站数和覆盖的长度,贪心选覆盖长度前k大的并每次更新

注意如果用高速能跑完相当于最后一个闭合了,答案+1

 1 #include<queue>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define lli long long
 6 using namespace std;
 7 const int N=3005;
 8 struct node
 9 {
10     lli tim;
11     int len,mxl;
12 };
13 bool operator < (node x,node y)
14 {
15     return x.len<y.len;
16 }
17 int n,m,k,a,b,c,ans,s[N];
18 lli T; priority_queue<node> hp;
19 void Insert(lli t,int l,int k)
20 {
21     if(t<0||l<0) return;
22     int cov=min(t/a,(lli)l)+1;
23     if(k) ans+=cov,Insert(t-1ll*cov*c,l-cov,0);
24     else hp.push((node){t,cov,l});
25 }
26 int main()
27 {
28     scanf("%d%d%d",&n,&m,&k),k-=m;
29     scanf("%d%d%d%lld",&a,&b,&c,&T);
30     for(int i=1;i<=m;i++) scanf("%d",&s[i]);
31     for(int i=1;i<m;i++) Insert(T-1ll*(s[i]-1)*b,s[i+1]-s[i]-1,1);
32     while(!hp.empty()&&k--)
33     {
34         node tn=hp.top(); hp.pop();
35         Insert(tn.tim,tn.mxl,1);
36     }
37     printf("%d",ans-1+(1ll*(s[m]-1)*b<=T));
38     return 0;
39 }
View Code

JOIOI王国

这种东西肯定是二分加检验,考虑怎么检验

我们发现割开最大值和最小值一定是最优的,那么我们四个方向都搞一搞,逐行找分界线即可

注意如果要满足限制4最后一定切出来是一个阶梯形(没想这个调了半天。。。)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=2005,inf=1e9;
 6 int n,m,mx,mn,rd,ans,s[4][N][N];
 7 bool Check(int t,int x)
 8 {
 9     int up=mx-x,dw=mn+x,b=m;
10     for(int i=1;i<=n;i++)
11     {
12         for(int j=1;j<=b;j++)
13             if(s[t][i][j]<up) {b=j-1;break;}
14         for(int j=b+1;j<=m;j++)
15             if(s[t][i][j]>dw) return false;
16     }
17     return true;
18 }
19 int Calc(int idx)
20 {
21     int l=mn,r=mx,ret=r;
22     while(l<=r)
23     {
24         int mid=(l+r)>>1;
25         if(Check(idx,mid)) r=mid-1,ret=mid;
26         else l=mid+1;
27     }
28     return ret;
29 }
30 int main()
31 {
32     scanf("%d%d",&n,&m),mn=ans=inf;
33     for(int i=1;i<=n;i++)
34         for(int j=1;j<=m;j++)
35         {
36             scanf("%d",&rd),mx=max(mx,rd),mn=min(mn,rd);
37             s[0][i][j]=s[1][j][n-i+1]=s[2][n-i+1][m-j+1]=s[3][m-j+1][i]=rd;
38         }
39     ans=min(ans,Calc(0)); swap(n,m);
40     ans=min(ans,Calc(1)); swap(n,m);
41     ans=min(ans,Calc(2)); swap(n,m);
42     ans=min(ans,Calc(3)); swap(n,m);
43     printf("%d",ans);
44     return 0;
45 }
View Code

足球

首先把题目抽象成最短路问题,然后发现直接建图边数爆炸了

优化是我们把每个位置拆点,分成“球员在控球”和“球正在向某个方向飞”这五个点,BFS预处理一下到某个点接住球的最小代价,然后就没了

 1 #include<queue>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define lli long long
 6 using namespace std;
 7 const int N=1260000,M=4100000,K=505;
 8 const int mov[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
 9 struct s{int node;lli dist;};
10 bool operator < (s x,s y)
11 {
12     return x.dist>y.dist;
13 }
14 priority_queue<s> hp;
15 pair<int,int> que[N]; lli val[M],dis[N];
16 int n,m,k,a,b,c,t1,t2,st,ed,hd,tl,cnt,tot;
17 int stp[K][K],idx[K][K],vis[N],p[N],noww[M],goal[M]; 
18 bool Check(int x,int y)
19 {
20     return x>=0&&y>=0&&x<=n&&y<=m;
21 }
22 void Link(int f,int t,lli v)
23 {
24     noww[++cnt]=p[f],p[f]=cnt;
25     goal[cnt]=t,val[cnt]=v;
26 }
27 void Dijkstra(int st)
28 {
29     memset(dis,0x3f,sizeof dis);
30     dis[st]=0,hp.push((s){st,0});
31     while(!hp.empty())
32     {
33         s t=hp.top(); hp.pop(); int tn=t.node;
34         if(vis[tn]) continue; vis[tn]=true;
35         for(int i=p[tn],g;i;i=noww[i])
36             if(dis[g=goal[i]]>dis[tn]+val[i])
37                 dis[g]=dis[tn]+val[i],hp.push((s){g,dis[g]});
38     }
39 }
40 int main()
41 {
42     register int i,j,h;
43     scanf("%d%d%d%d%d%d",&n,&m,&a,&b,&c,&k),tot=1;
44     for(i=0;i<=n;i++)
45         for(j=0;j<=m;j++) idx[i][j]=tot,tot+=5;
46     hd=0,tl=-1,memset(stp,-1,sizeof stp);
47     for(i=1;i<=k;i++) 
48     {
49         scanf("%d%d",&t1,&t2);
50         stp[t1][t2]=0,que[++tl]=make_pair(t1,t2);
51         if(i==1) st=idx[t1][t2]; if(i==k) ed=idx[t1][t2];
52     }
53     while(hd<=tl)
54     {
55         pair<int,int> pr=que[hd++];
56         for(i=0;i<4;i++)
57         {
58             int xx=pr.first,yy=pr.second;
59             int tx=xx+mov[i][0],ty=yy+mov[i][1];
60             if(Check(tx,ty)&&stp[tx][ty]==-1) 
61             {
62                 stp[tx][ty]=stp[xx][yy]+1;
63                 que[++tl]=make_pair(tx,ty);
64             }
65         }
66     }
67     for(i=0;i<=n;i++)
68         for(j=0;j<=m;j++)
69         {
70             int nde=idx[i][j];
71             for(h=0;h<4;h++)
72             {
73                 Link(nde+h+1,nde,1ll*stp[i][j]*c);
74                 int tx=i+mov[h][0],ty=j+mov[h][1];
75                 if(Check(tx,ty))
76                 {
77                     Link(nde,idx[tx][ty],c);
78                     Link(nde+h+1,idx[tx][ty]+h+1,a);
79                     Link(nde,idx[tx][ty]+h+1,a+b);
80                 }
81             }
82         }
83     Dijkstra(st); long long ans=1e18;
84     for(i=0;i<=4;i++) ans=min(ans,dis[ed+i]);
85     printf("%lld",ans);    
86     return 0;
87 }
View Code

首先观察到一开始先染好色再折叠和边折叠边染色是一样的

先给出结论:对于每个颜色,我们令奇数根绳子分别尝试和左边或右边的绳子配对,然后统计颜色的众数,答案即 总数-当前颜色的绳数-众数,两种方式取min即是答案

方案最优的正确性显然(最后保留的就是当前颜色和众数)

方案存在的正确性是因为我们硬点最后剩下当前颜色和区间众数,就变成了一个01序列,于是没有对不上的情况了(注意这里说的一个0或者1是两个数合起来的)

 1 #include<cstdio>
 2 #include<vector>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=1e6+60;
 6 vector<int> ve[N];
 7 int n,m,mx,col[N],bkt[N],app[N];
 8 void Add(int c){--app[bkt[c]],++app[++bkt[c]]; mx=max(mx,bkt[c]);}
 9 void Del(int c){--app[bkt[c]],++app[--bkt[c]]; if(!app[mx]) mx--;}
10 int Oth(int x){return (x&1)?(x+1):(x-1);}
11 int main()
12 {
13     scanf("%d%d",&n,&m);
14     for(int i=1;i<=n;i++)
15     {
16         scanf("%d",&col[i]);
17         ve[col[i]].push_back(i),Add(col[i]);
18     }
19     for(int i=1;i<=m;i++)
20     {
21         col[0]=col[n+1]=i;
22         int tot=ve[i].size(),ans=n;
23         for(int j=0;j<tot;j++) Del(i);
24         for(int j=0;j<tot;j++) 
25             if(col[Oth(ve[i][j])]!=i) 
26                 Del(col[Oth(ve[i][j])]);
27         ans=min(ans,n-tot-mx);
28         for(int j=0;j<tot;j++) 
29         {
30             if(col[Oth(ve[i][j])]!=i) 
31                 Add(col[Oth(ve[i][j])]);
32             if(col[ve[i][j]^1]!=i)
33                 Del(col[ve[i][j]^1]);
34         }
35         ans=min(ans,n-tot-mx);
36         for(int j=0;j<tot;j++) 
37             if(col[ve[i][j]^1]!=i)
38                 Add(col[ve[i][j]^1]);
39         for(int j=0;j<tot;j++) Add(i);
40         printf("%d\n",ans);
41     }
42     return 0;
43 }
View Code

JOI 2017 Final 完结撒花


 

JOI 2018 Final

寒冬暖炉

真实普及组题,贪心一下就莫得了

这题都WA两次,你说这人还怎么救啊

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=100005;
 6 int n,k,rd,lst,cnt,ans,len[N];
 7 int main()
 8 {
 9     scanf("%d%d%d",&n,&k,&lst),ans=n;
10     for(int i=2;i<=n;i++)
11     {
12         scanf("%d",&rd);
13         if(lst+1!=rd) len[++cnt]=rd-lst-1; lst=rd;
14     } 
15     sort(len+1,len+1+cnt); 
16     for(int i=1;i<=cnt;i++) ans+=len[i];
17     for(int i=1;i<k;i++) ans-=len[cnt-i+1];
18     printf("%d",ans);
19     return 0;
20 }
View Code

美术展览

真实普及题,拆式子然后扫一遍

感觉自己在颓普及组题,没救了

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=500005;
 6 struct a{long long a,b;}o[N];
 7 bool cmp(a x,a y)
 8 {
 9     return x.b<y.b;
10 }
11 long long n,t1,t2,mn,ans,s[N],x[N],y[N];
12 int main()
13 {
14     scanf("%lld",&n);
15     for(int i=1;i<=n;i++) 
16         scanf("%lld%lld",&t1,&t2),o[i]=(a){t2,t1}; 
17     sort(o+1,o+1+n,cmp);
18     for(int i=1;i<=n;i++)
19     {
20         s[i]=s[i-1]+o[i].a;
21         x[i]=s[i-1]-o[i].b,y[i]=s[i]-o[i].b;
22     }
23     ans=y[1],mn=x[1];
24     for(int i=2;i<=n;i++)
25         mn=min(mn,x[i]),ans=max(ans,y[i]-mn);
26     printf("%lld",ans);
27     return 0;
28 }
View Code

团子制作

奇怪的DP,注意题目要求有顺序而且必须是严格左到右和上到下(不然反正我不会做=。=)

沿着斜对角线DP,三位DP一个记横着的一个记竖着的一个当中间变量从上两个(当前格子横着插,上一个竖着插,这两个冲突所以要取一个优的)里取最优的

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=3005;
 6 int n,m,x,y,ans,dp[3]; 
 7 char tuan[N][N];
 8 bool oo1(int x,int y)
 9 {
10     return tuan[x][y]=='R'&&tuan[x][y+1]=='G'&&tuan[x][y+2]=='W';
11 }
12 bool oo2(int x,int y)
13 {
14     return tuan[x][y]=='R'&&tuan[x+1][y]=='G'&&tuan[x+2][y]=='W';
15 }
16 int main()
17 {
18     scanf("%d%d",&n,&m);
19     for(int i=1;i<=n;i++) scanf("%s",tuan[i]+1);
20     for(int i=1;i<=n+m-1;i++)
21     {
22         i<=m?(x=1,y=i):(x=i-m+1,y=m);
23         memset(dp,0,sizeof dp);
24         while(x<=n&&y>=1)
25         {
26             dp[0]=max(dp[1],dp[0]+oo1(x,y));
27             dp[1]=max(dp[0],dp[2]);
28             dp[2]=max(dp[1],dp[2]+oo2(x,y));
29             x++,y--;
30         }
31         ans+=dp[2];
32     }
33     printf("%d",ans);
34     return 0;
35 }
View Code

月票购买

把s到t最短路上的边提出来搞成零再跑u到v的最短路,然后发现自己想水了,显然这样可能会走两条路,不合法

但是可以部分借鉴这个思路,发现最后一定是先从u出发走一段,然后在选的最短路上走一段,最后再从最短路上离开走到t。于是预处理从四个点出发的最短路,从s开始一次DFS把路径拼起来。

数组玄学开小,最后只能无脑调大,wsl

 1 #include<queue>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int M=400005;
 7 struct a
 8 {
 9     int node; 
10     long long dist;
11 };
12 bool operator < (a x,a y)
13 {
14     return x.dist>y.dist;
15 }
16 priority_queue<a> hp;
17 int n,m,u,v,w,s1,t1,s2,t2,cnt;
18 int p[M],noww[M],goal[M],vis[M];
19 long long val[M],mnu[M],mnv[M];
20 long long dis1[M],dis2[M],dis3[M],dis4[M];
21 void Link(int f,int t,int v)
22 {
23     noww[++cnt]=p[f],p[f]=cnt;
24     goal[cnt]=t,val[cnt]=v;
25     noww[++cnt]=p[t],p[t]=cnt;
26     goal[cnt]=f,val[cnt]=v;
27 }
28 void Dijkstra(int s,long long *dis)
29 {
30     memset(vis,0,sizeof vis);
31     memset(dis,0x3f,sizeof vis);
32     dis[s]=0,hp.push((a){s,0});
33     while(!hp.empty())
34     {
35         a t=hp.top(); hp.pop(); int tn=t.node;
36         if(vis[tn]) continue; vis[tn]=true;
37         for(int i=p[tn],g;i;i=noww[i])
38             if(dis[g=goal[i]]>dis[tn]+val[i])
39                 dis[g]=dis[tn]+val[i],hp.push((a){g,dis[g]});
40     }
41 }
42 void DFS(int nde)
43 {
44     vis[nde]=true,mnu[nde]=dis3[nde],mnv[nde]=dis4[nde];
45     for(int i=p[nde],g;i;i=noww[i])
46         if(dis1[nde]+dis2[g=goal[i]]+val[i]==dis1[t1])
47         {
48             if(!vis[g]) DFS(g);
49             if(mnu[nde]+mnv[nde]>min(dis3[nde],mnu[g])+min(dis4[nde],mnv[g]))
50                 mnu[nde]=min(dis3[nde],mnu[g]),mnv[nde]=min(dis4[nde],mnv[g]);
51         }
52 }
53 int main()
54 {
55     scanf("%d%d",&n,&m),cnt=1;
56     scanf("%d%d%d%d",&s1,&t1,&s2,&t2);
57     for(int i=1;i<=m;i++)
58         scanf("%d%d%d",&u,&v,&w),Link(u,v,w);
59     Dijkstra(s1,dis1),Dijkstra(t1,dis2);
60     Dijkstra(s2,dis3),Dijkstra(t2,dis4);
61     memset(vis,0,sizeof vis),DFS(s1);
62     printf("%lld",min(mnu[s1]+mnv[s1],dis3[t2]));
63     return 0;
64 }
View Code

毒蛇越狱

事实证明逃课是早晚要补的

发现$min('1','0'',?')<=6$,在这上面做文章

首先'?'少的时候直接暴力枚举即可

考虑'0'少的时候,用超集和容斥

考虑‘1’少的时候,用子集和容斥

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=21,M=(1<<20)+20;
 6 int n,q,all,sub[M],sup[M],sum[M],cnt[M]; char str[M];
 7 int main()
 8 {
 9     scanf("%d%d%s",&n,&q,str),all=(1<<n)-1;
10     for(int i=0;i<=all;i++) 
11         sub[i]=sup[i]=sum[i]=str[i]-'0',cnt[i]=cnt[i>>1]+(i&1);
12     for(int i=0;i<n;i++)
13         for(int j=0,k=1<<i;j<=all;j++)    
14             if(!(j&k)) sub[j|k]+=sub[j],sup[j]+=sup[j|k];
15     while(q--)
16     {
17         scanf("%s",str);
18         int s1=0,s2=0,s3=0,ans=0;
19         for(int i=0,t;i<n;i++)
20             t=1<<(n-i-1),str[i]=='0'?(s1|=t):(str[i]=='1'?(s2|=t):(s3|=t));
21         if(cnt[s1]<=6) 
22             for(int i=s1;;i=(i-1)&s1)
23                 {ans+=(cnt[i]&1)?-sup[i|s2]:sup[i|s2]; if(!i) break;}
24         else if(cnt[s2]<=6)
25             for(int i=s2;;i=(i-1)&s2)
26                 {ans+=(cnt[i^s2]&1)?-sub[i|s3]:sub[i|s3]; if(!i) break;}
27         else if(cnt[s3]<=6)
28             for(int i=s3;;i=(i-1)&s3)
29                 {ans+=sum[i|s2]; if(!i) break;}
30         printf("%d\n",ans);
31     }
32     return 0;
33 }
View Code

JOI 2018 Final 完结撒花


 

感觉到了一丝丝不对劲,是不是JOISC比JOI Final要难啊。。。。。。wsl

 

转载于:https://www.cnblogs.com/ydnhaha/p/10618048.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值