暑期集训第七天(6-28)题解及总结
今天因为昨天作死写替罪羊树被教练吵了......我再也不好高骛远了QAQ。
今天一如既往的进行了考试,今天的题没有原题,除了一道学长出的教练忘掉的题之外,其他的题都是第一次见,今天也是我写暴力写的题占比最大的一天(因为正解思路想不到),说明还是得练呀,水平还是不高,今天我们四个人中三个人的名次都相对进步了,只有我退步了一名......
这道题的数只有4*4=16,应该可以想到状压dp,但是去压哪一个就是我们所思考的问题了,我刚开始的时候想的是去压每一个位置是黑面还是白面,但是我们的转移就不好进行,因为后面的处理结果可能正是前面的运算所需要的,即有后效性,这是不符合dp的使用原则的,于是我们换一种思路,来用状压表示是否对原状态的几位进行反转操作,这样我们前后的计算就互不影响,可以进行dp的,其实这道题还是挺简单的,虽然我只有50pts
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 const int maxn=4+5,Inf=1<<16; 3 char str[maxn][maxn]; 4 int ans=Inf; 5 char tmp[maxn][maxn]; 6 void flip(char &ch){ 7 if(ch=='w')ch='b'; 8 else if(ch=='b')ch='w'; 9 } 10 int cnt; 11 void press(int x,int y){ 12 cnt++; 13 flip(tmp[x][y]); 14 flip(tmp[x][y+1]);flip(tmp[x][y-1]); 15 flip(tmp[x-1][y]);flip(tmp[x+1][y]); 16 } 17 void calc(int x,char ch){ 18 memcpy(tmp,str,sizeof(str)); 19 cnt=0; 20 for(int i=1;i<=4;i++) 21 if(x & (1<<(i-1))) 22 press(1,i); 23 for(int i=2;i<=4;++i){ 24 for(int j=1;j<=4;++j){ 25 if(tmp[i-1][j]!=ch) 26 press(i,j); 27 } 28 } 29 for(int i=1;i<=4;++i) 30 if(tmp[4][i]!=ch)return; 31 ans=std::min(ans,cnt); 32 33 } 34 void Solve(){ 35 for(int i=1;i<=4;++i) 36 scanf("%s",str[i]+1); 37 for(int i=0;i<=15;++i){ 38 calc(i,'w'); 39 calc(i,'b'); 40 } 41 if(ans==Inf) 42 printf("Impossible\n"); 43 else 44 printf("%d\n",ans); 45 } 46 int main(){ 47 Solve(); 48 return 0; 49 }
一打开这道题,咦?这个图怎么这么眼熟,又一读题,就认识到这道题曾经在寒假集训的时候被学长出在元宵节欢乐颂题目练习里面,这道题其实对现在的我们来说不难,读完题后思路应该就有了:tarjan缩点+dfs/拓扑排序/SPFA,拓扑排序不怎么熟,SPFA被玩梗玩死了,于是我写的是dfs,又怕他超时,加了一个记忆化减枝,但是还是T了,但是比其他写dfs的人多15分...(记忆化万岁!!!),原本我以为我在也不会写SPFA了,但是这道题也只能用SPFA来解决了,其他就没有什么好注意的了.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=6e5+10; 4 struct Node{ 5 int from,next,to; 6 }edge[N<<1]; 7 int Head[N],tot; 8 int end[N],money[N]; 9 void Add(int x,int y){ 10 edge[++tot].from=x; 11 edge[tot].to=y; 12 edge[tot].next=Head[x]; 13 Head[x]=tot; 14 } 15 int dfn[N],low[N],dfn_cnt,scc_cnt,belong[N],siz[N]; 16 stack<int>sta; 17 void tarjan(int u){ 18 dfn[u]=low[u]=++dfn_cnt; 19 sta.push(u); 20 for(int i=Head[u];i;i=edge[i].next){ 21 int v=edge[i].to; 22 if(!dfn[v]){ 23 tarjan(v); 24 low[u]=min(low[u],low[v]); 25 } 26 else if(!belong[v]) low[u]=min(low[u],dfn[v]); 27 } 28 if(dfn[u]==low[u]){ 29 scc_cnt++; 30 int t; 31 do{ 32 t=sta.top();sta.pop(); 33 belong[t]=scc_cnt; 34 //printf("belong[%d]=%d\n",t,scc_cnt); 35 siz[scc_cnt]+=money[t]; 36 }while(t!=u); 37 } 38 } 39 int vis[N],dis[N]; 40 int ans; 41 queue<int>q; 42 void Spfa(int x){ 43 memset(vis,0,sizeof(vis)); 44 memset(dis,0,sizeof(dis)); 45 vis[x]=true;dis[x]=siz[x]; 46 q.push(x); 47 while(!q.empty()){ 48 int u=q.front();q.pop(); 49 vis[u]=false; 50 for(int i=Head[u];i;i=edge[i].next){ 51 int v=edge[i].to; 52 if(v==3){ 53 } 54 if(dis[v]<dis[u]+siz[v]){ 55 dis[v]=dis[u]+siz[v]; 56 if(!vis[v]){ 57 //printf("%d\n",v); 58 vis[v]=true; 59 q.push(v); 60 } 61 } 62 } 63 } 64 } 65 int main(){ 66 //freopen("a.in","r",stdin); 67 //freopen("a.out","w",stdout); 68 int n,m; 69 scanf("%d%d",&n,&m); 70 for(int i=1;i<=m;++i){ 71 int x,y; 72 scanf("%d%d",&x,&y); 73 Add(x,y); 74 } 75 for(int i=1;i<=n;++i) scanf("%d",&money[i]); 76 int root,t; 77 scanf("%d%d",&root,&t); 78 tarjan(root); 79 while(t--){ 80 int x;scanf("%d",&x); 81 end[belong[x]]=1; 82 } 83 int cnt=tot;tot=0; 84 memset(Head,0,sizeof(Head)); 85 for(int i=1;i<=cnt;++i){ 86 int x=edge[i].from,y=edge[i].to; 87 if(belong[x]!=belong[y]) Add(belong[x],belong[y]); 88 } 89 Spfa(belong[root]); 90 int ans=0; 91 for(int i=1;i<=scc_cnt;++i){ 92 if(end[i]) ans=max(ans,dis[i]); 93 } 94 printf("%d\n",ans); 95 return 0; 96 }
这道题其实是一个线性dp...在考试的时候我只猜对了一半,我从题干的描述中认为他是一个区间dp,由于大部分的区间dp都能暴搜,于是就没有考虑其他做法,但是时间效率还是很可怕的,100!的时间效率毕竟吃不住,如果是线性dp,我们考虑dp[i][j]表示当前从前j位置中取出i的数字的最优误差,所以我们考虑状态转移:dp[i][j]=min(dp[i][j],dp[k][j−1]−back[k]+back[i]+cost[k][i]),其中back[i]表示在i之后不再取数时的花费,front表示在i之前不再取数时的花费,cost[i][j]表示取出i,j两点的时候之间两点的花费,之后就可以n^3来解决这道问题啦(比n!的dfs要快的多)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 int cost[110][110],front[110],back[110]; 5 int n,m,a[110]; 6 int ans,Min=0x3f3f3f3f3f3f3f3f,dp[110][110]; 7 signed main(){ 8 //freopen("a.in","r",stdin); 9 //freopen("a.out","w",stdout); 10 scanf("%lld%lld",&n,&m); 11 ans=n; 12 for(int i=1;i<=n;++i){ 13 scanf("%lld",&a[i]); 14 } 15 for(int i=1;i<=n;++i){ 16 for(int j=1;j<i;++j){ 17 front[i]+=2*abs(a[j]-a[i]); 18 } 19 for(int j=n;j>i;--j){ 20 back[i]+=2*abs(a[j]-a[i]); 21 } 22 for(int j=1;j<i;++j){ 23 for(int k=j+1;k<i;++k){ 24 cost[j][i]+=abs(2*a[k]-(a[i]+a[j])); 25 } 26 } 27 } 28 memset(dp,0x3f,sizeof(dp)); 29 for(int i=1;i<=n;++i){ 30 dp[1][i]=front[i]+back[i]; 31 if(dp[1][i]<m){ 32 Min=min(Min,dp[1][i]); 33 } 34 } 35 if(Min!=0x3f3f3f3f3f3f3f3f){ 36 printf("1 %lld\n",Min); 37 exit(0); 38 } 39 for(int i=2;i<=n;++i) 40 for(int j=i;j<=n;++j){ 41 for(int k=i-1;k<j;++k){ 42 dp[i][j]=min(dp[i][j],dp[i-1][k]-back[k]+cost[k][j]+back[j]); 43 } 44 if(dp[i][j]<=m){ 45 if(ans>i){ 46 ans=i; 47 Min=dp[i][j]; 48 } 49 if(ans==i){ 50 Min=min(Min,dp[i][j]); 51 } 52 } 53 } 54 printf("%lld %lld\n",ans,Min); 55 return 0; 56 }
这个题目背景是谁改的,这么有才......
这道题的思维量还是有的,毕竟我只会写暴力水分,题目中要求中位数最大,则我们可以先对猪的智慧进行从小到大的排序,之后对于每一个可以成为中位数的点(即距两边距离n/2),计算其两边各取n/2个数字,并尽量使之之和最小,如果小于总基金,则考虑记录答案,则重点就是处理两边的最小/大值,我们可以用一个大根堆来维护当前两边的最大值,然后向左/右进行扫描,如果当前数字小于队顶,则进行替换,来达到尽可能小的目的,然后我们就可以进行枚举一个点两边的左边和右边的最小值,记录和小于基金的答案就可以了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define int long long 4 const int N=1e6+10; 5 int l[N],r[N]; 6 struct Node{ 7 int val,cost; 8 bool operator < (const Node &a)const{ 9 return a.val < val; 10 } 11 }pig[N]; 12 priority_queue<int>q; 13 signed main(){ 14 //freopen("a.in","r",stdin); 15 int n,c,m; 16 scanf("%lld%lld%lld",&n,&c,&m); 17 for(int i=1;i<=c;++i){ 18 scanf("%lld%lld",&pig[i].val,&pig[i].cost); 19 l[i]=r[i]=0x7fffffff; 20 } 21 sort(pig+1,pig+1+c); 22 int sum=0; 23 for(int i=1;i<=n/2;++i){ 24 sum+=pig[i].cost; 25 q.push(pig[i].cost); 26 } 27 for(int i=n/2+1;i<=c;++i){ 28 l[i]=sum; 29 if(pig[i].cost < q.top()){ 30 sum-=q.top(); q.pop(); 31 sum+=pig[i].cost; 32 q.push(pig[i].cost); 33 } 34 } 35 while(!q.empty()) q.pop(); 36 sum=0; 37 for(int i=c;i>=c-n/2+1;--i){ 38 sum+=pig[i].cost; 39 q.push(pig[i].cost); 40 } 41 for(int i=c-n/2;i>=1;--i){ 42 r[i]=sum; 43 if(pig[i].cost < q.top()){ 44 sum-=q.top(); q.pop(); 45 sum+=pig[i].cost; 46 q.push(pig[i].cost); 47 } 48 } 49 int ans=0; 50 for(int i=n/2+1;i<=c-n/2;++i){ 51 if(l[i]+r[i]+pig[i].cost<=m){ 52 ans=max(ans,pig[i].val); 53 } 54 } 55 if(!ans) 56 printf("-1\n"); 57 else printf("%lld\n",ans); 58 return 0; 59 }
tarjan板子,直接建好图缩点,记录每个联通块内的最小值,以及最小值的个数,那么第一个答案就是最小值之和,第二个答案就是每个联通块之内的最小值个数的乘积,就好了(这道题数据是真的水,不用开longlong,不用取模,甚至直接把大小加起来并和1一起输出都有50pts)竞速题,没能超过郭大佬QAQ
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 const int N=1e6+10; 3 using namespace std; 4 struct Node{ 5 int next,to; 6 }edge[N]; 7 int Head[N],tot; 8 void Add(int x,int y){ 9 edge[++tot].to=y; 10 edge[tot].next=Head[x]; 11 Head[x]=tot; 12 } 13 int dfn[N],low[N],dfn_cnt,scc_cnt,belong[N],siz[N],cost[N]; 14 stack<int>sta; 15 void tarjan(int u){ 16 dfn[u]=low[u]=++dfn_cnt; 17 sta.push(u); 18 for(int i=Head[u];i;i=edge[i].next){ 19 int v=edge[i].to; 20 if(!dfn[v]){ 21 tarjan(v); 22 low[u]=min(low[u],low[v]); 23 } 24 else if(!belong[v]) low[u]=min(low[u],dfn[v]); 25 } 26 if(dfn[u]==low[u]){ 27 scc_cnt++; 28 siz[scc_cnt]=0x7fffffff; 29 int t; 30 do{ 31 t=sta.top();sta.pop(); 32 belong[t]=scc_cnt; 33 siz[scc_cnt]=min(siz[scc_cnt],cost[t]); 34 }while(t!=u); 35 } 36 } 37 int an[N]; 38 int main(){ 39 int n,m; 40 scanf("%d",&n); 41 for(int i=1;i<=n;++i) scanf("%d",&cost[i]); 42 scanf("%d",&m); 43 for(int i=1;i<=m;++i){ 44 int x,y; 45 scanf("%d%d",&x,&y); 46 Add(x,y); 47 } 48 for(int i=1;i<=n;++i) 49 if(!dfn[i]) tarjan(i); 50 int ans1=0; 51 for(int i=1;i<=scc_cnt;++i) ans1+=siz[i]; 52 for(int i=1;i<=n;++i){ 53 if(siz[belong[i]]==cost[i]) an[belong[i]]++; 54 } 55 int ans2=1; 56 for(int i=1;i<=scc_cnt;++i) ans2*=an[i]; 57 printf("%d %d\n",ans1,ans2); 58 return 0; 59 }
这道题是下午改完题后和大佬们一起写的,起初都觉得和tarjan没有什么关系,lc甚至说是一道最短路(虽然他真的用最短路拿到了我们四个人的首A),然而我们并不赞同,于是yy出了各种奇葩的想法,在吃完饭后,我们都才依次的把这道题解决。
我们首先处理输出NO的情况,对输入的图统计出度为零的点,如果该点出度为零且不能收买,那就直接跑一边dfs,找到无法通到的最小的点输出退出程序即可,重点在YES的处理上(NO的好像只有一个点???),由于可能出现环,考虑进行tarjan缩点,统计一下每个联通块之内可以收买的最小花费(买一个一个环的人就都没了)之后再次统计入度为零的点,记录答案总和,输出即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1e6+10; 4 struct Node{ 5 int next,to,from; 6 }edge[N]; 7 int Head[N],tot; 8 void Add(int x,int y){ 9 edge[++tot].to=y; 10 edge[tot].from=x; 11 edge[tot].next=Head[x]; 12 Head[x]=tot; 13 } 14 struct Jiandie{ 15 int id,cost; 16 }a[N]; 17 bool buy[N]; 18 int vis[N]; 19 void dfs(int u){ 20 vis[u]=1; 21 for(int i=Head[u];i;i=edge[i].next){ 22 int v=edge[i].to; 23 if(!vis[v]) dfs(v); 24 } 25 } 26 int dfn[N],low[N],dfn_cnt,belong[N],siz[N],scc_cnt,cost[N]; 27 stack<int>sta; 28 void tarjan(int u){ 29 dfn[u]=low[u]=++dfn_cnt; 30 sta.push(u); 31 for(int i=Head[u];i;i=edge[i].next){ 32 int v=edge[i].to; 33 if(!dfn[v]){ 34 tarjan(v); 35 low[u]=min(low[u],low[v]); 36 } 37 else if(!belong[v]) low[u]=min(low[u],dfn[v]); 38 } 39 if(dfn[u]==low[u]){ 40 scc_cnt++; 41 siz[scc_cnt]=0x7fffffff; 42 int t; 43 do{ 44 t=sta.top();sta.pop(); 45 belong[t]=scc_cnt; 46 if(cost[t]) siz[scc_cnt]=min(siz[scc_cnt],cost[t]); 47 }while(t!=u); 48 } 49 } 50 int ru[N]; 51 int main(){ 52 int n,m; 53 scanf("%d%d",&n,&m); 54 for(int i=1;i<=m;++i){ 55 scanf("%d%d",&a[i].id,&a[i].cost); 56 buy[a[i].id]=1; 57 cost[a[i].id]=a[i].cost; 58 } 59 int t; 60 scanf("%d",&t); 61 while(t--){ 62 int x,y; 63 scanf("%d%d",&x,&y); 64 Add(x,y);ru[y]++; 65 } 66 bool flag=0; 67 for(int i=1;i<=n;++i){ 68 if(ru[i]) continue; 69 if(!buy[i]) flag=1; 70 } 71 if(flag){ 72 printf("NO\n"); 73 for(int i=1;i<=m;++i) 74 dfs(a[i].id); 75 for(int i=1;i<=n;++i){ 76 if(!vis[i]){ 77 printf("%d\n",i); 78 exit(0); 79 } 80 } 81 } 82 for(int i=1;i<=n;++i){ 83 if(!dfn[i]) tarjan(i); 84 } 85 memset(ru,0,sizeof(ru)); 86 for(int i=1;i<=tot;++i){ 87 int x=edge[i].from,y=edge[i].to; 88 if(belong[x]!=belong[y]) ru[belong[y]]++; 89 } 90 printf("YES\n"); 91 int ans=0; 92 for(int i=1;i<=scc_cnt;++i){ 93 if(!ru[i] && siz[i]!=0x7fffffff) ans+=siz[i]; 94 } 95 printf("%d\n",ans); 96 return 0; 97 }
总结:
今天晚上gyz大佬和我们一起刷题,感觉整个人都紧张起来了,dzn也表示跟打比赛一样(虽然最后一个小时变成了集体颓,可能前面竞争太狠了吧),我从lc那里嫖来了页面SCC配置(我们这一片基本就我没弄了)希望明天的晚上可以提高自己的效率吧,集训一天天的进行着,也不希望自己一天天的颓下去,希望明天的考试可以取得一个好成绩吧。