“科大讯飞杯”第18届上海大学程序设计联赛春季赛暨高校网络友谊赛
鸽了快一个月的题解
A. 组队比赛 (Nowcoder 5278 A)
题目大意
给定,俩俩分组,使得两组中数的和的差最小。
解题思路
最小最大一组另外的一组即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int a[4]; cin>>a[0]>>a[1]>>a[2]>>a[3]; sort(a,a+4); int ans=abs(a[0]+a[3]-a[1]-a[2]); cout<<ans<<endl; return 0; }
B. 每日一报 (Nowcoder 5278 B)
题目大意
符合条件的结构体排序。
解题思路
符合条件的结构体排序。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; const double eps=1e-5; struct data{ int ri,num; double t; bool operator < (const data & a){ if (ri>a.ri) return true; if (ri<a.ri) return false; if (t-a.t>eps) return true; if (t-a.t<eps) return false; if (num<a.num) return true; return false; } }; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); vector<data> qwq; int n; cin>>n; double tt; for(int r,nu,i=1;i<=n;++i){ cin>>r>>nu>>tt; if (tt-38.0<-eps) continue; qwq.push_back({r,nu,tt}); } sort(qwq.begin(),qwq.end()); cout<<qwq.size()<<endl; for(auto i:qwq){ cout<<i.ri<<' '<<i.num<<' '<<fixed<<setprecision(1)<<i.t<<endl; } return 0; }
C. 最长非公共子序列 (Nowcoder 5278 C)
题目大意
给定,求出最长的非公共子序列的长度,不存在输出。
解题思路
相等即为否则为长度最大的那个。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); string s1,s2; cin>>s1>>s2; if (s1==s2) cout<<"-1"<<endl; else cout<<max(s1.size(),s2.size())<<endl; return 0; }
D. 最大字符集 (Nowcoder 5278 D)
题目大意
给定一个,求一个集合,满足以下条件:
- 每个字符串由 0 和 1 组成。
- 每个字符串长度在 1 到 n 之间,且两两长度不同。
- 集合中任何一个字符串都不是其他字符串的子串。
最大化集合元素个数,输出任意一种可能的集合情况。
解题思路
构造题。特殊处理,其余的最大个数为,长度从到,每个元素首尾为中间为。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin>>n; if (n==1){ cout<<"1"<<endl; cout<<"1"<<endl; }else if (n==2){ cout<<"2"<<endl; cout<<"1"<<endl; cout<<"00"<<endl; }else{ cout<<n-1<<endl; for(int i=2;i<=n;++i){ string s(i,'0'); s[0]='1'; s[i-1]='1'; cout<<s<<endl; } } return 0; }
E. 美味的序列 (Nowcoder 5278 E)
题目大意
给定一个序列,每次可以从头或尾取一个数,每次取后序列里的所有数减一。问最优的取法,取出来的数的和是多少。
解题思路
很容易发现怎么取结果都是一样的(将减一操作作用在另一个初始全为0的数组),于是答案就是所有数的和减去。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin>>n; LL ans=-(LL)(n)*(n-1)/2; LL qwq=0; while(n--){ cin>>qwq; ans+=qwq; } cout<<ans<<endl; return 0; }
F. 日期小助手 (Nowcoder 5278 F)
题目大意
给定一个日期,输出该日期后最近的母亲节或父亲节是什么时候。
解题思路
求出当前年和下一年的母亲父亲节日期比较即可。
恰好练一练类。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; struct data{ int y,m,d; bool operator < (const data & a){ if (y<a.y) return true; if (y>a.y) return false; if (m<a.m) return true; if (m>a.m) return false; if (d<a.d) return true; if (d>a.d) return false; return false; } }; int run[108]={0}; bool check(int y){ if (y%400==0) return true; if (y%100!=0&&y%4==0) return true; return false; } pair<data,data> solve(int y){ int x=6-(y-2000+run[y-2000]); int xx=3-(y-2000+run[y-2000]); while(x<0){ x+=7; } while(xx<0){ xx+=7; } data a={y,5,x+8}; data b={y,6,xx+15}; return (make_pair(a,b)); } int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); for(int i=1;i<=105;++i) if (check(i+2000)) run[i]++; for(int i=1;i<=101;++i) run[i]+=run[i-1]; int t; cin>>t; while(t--){ data noww; cin>>noww.y>>noww.m>>noww.d; auto qwq=solve(noww.y); auto qaq=solve(noww.y+1); if (noww<qwq.first){ cout<<"Mother's Day: May "<<qwq.first.d<<"th, "<<qwq.first.y<<endl; }else if (noww<qwq.second){ cout<<"Father's Day: June "<<qwq.second.d; if (qwq.second.d==21) cout<<"st, "; else cout<<"th, "; cout<<qwq.second.y<<endl; }else{ cout<<"Mother's Day: May "<<qaq.first.d<<"th, "<<qaq.first.y<<endl; } } return 0; }
G. 血压游戏 (Nowcoder 5278 G)
题目大意
给定一棵树,根的编号是(从开始),每个节点有只松鼠,它们不断向根节点移动,且会依次发生以下事件:
- 如果一个节点上有 2 只或 2 只以上的松鼠,他们会打架,然后这个节点上松鼠的数量会减少 1;
- 根节点的所有松鼠移动到地面,位于地面上的松鼠不会再打架;
- 所有松鼠同时朝它们的父节点移动。
问最终有多少只松鼠到达地面。
解题思路
容易发现不同深度之间的松鼠互不干扰,所以我们每次就针对同一深度的松鼠考虑。
表示到达节点的松鼠数量,很容易可以转移,但每个深度的求解复杂度是,最坏情况即链为会炸。
但我们发现松鼠在往根跑的时候好多情况是一样的(即减少的松鼠数为深度的减少数),只是在多处松鼠的所谓的"汇聚点"即处会发生变化。于是我们可以可以把树压缩,保留那些关键的转移点(即),去掉那些可以预知变化的点(即起始点到LCA间的点)。
其实这就是要建一棵虚树,用栈维护当前建的一条从根节点开始的链。虚树。
最终复杂度即为。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } struct data{ int deep,t,id; bool operator < (const data & a) const{ if (deep<a.deep) return true; if (deep>a.deep) return false; if (t<a.t) return true; return false; } }; const int N=2e5+8; vector<data> po; vector<int> edge[N]; int up[N][32]; LL dp[N]; int deep[N]; int n,s,t; LL cnt[N]; LL ans; void DFS(int u,int fa){ deep[u]=deep[fa]+1; po.push_back({deep[u],++t,u}); up[u][0]=fa; for(auto v:edge[u]){ if (v==fa) continue; DFS(v,u); } } int lca(int u,int v){ if (u==v) return u; if (deep[u]<deep[v]) swap(u,v); for(int i=31;i>=0;--i){ if (deep[up[u][i]]>=deep[v]) u=up[u][i]; } if (u==v) return u; for(int i=31;i>=0;--i){ if (up[u][i]!=up[v][i]){ u=up[u][i]; v=up[v][i]; } } return up[u][0]; } int st[N],top; int head[N],to[N*2],nxt[N*2],num; void add(int u,int v){ num++; nxt[num]=head[u]; to[num]=v; head[u]=num; } void DP(int u,int fa){ for(int v,i=head[u];i;i=nxt[i]){ v=to[i]; if (v==fa) continue; DP(v,u); if (dp[v]) dp[u]+=max(1ll,dp[v]-deep[v]+deep[u]+1); dp[v]=0; head[u]=0; } if (dp[u]) dp[u]=max(1ll,dp[u]-1); } void solve(int l,int r){ num=0; top=0; st[top]=s; int la=s; for(int i=l;i<=r;++i){ dp[po[i].id]=cnt[po[i].id]; int cur=po[i].id; int fa=lca(la,cur); while(top>0&&deep[st[top-1]]>=deep[fa]){ add(st[top-1],st[top]); --top; } if (st[top]!=fa){ add(fa,st[top]); st[top]=fa; } st[++top]=cur; la=cur; } while(top>0){ add(st[top-1],st[top]); --top; } DP(s,s); ans+=dp[s]; dp[s]=0; } int main(){ read(n); read(s); for(int i=1;i<=n;++i) read(cnt[i]); for(int u,v,i=1;i<n;++i){ read(u); read(v); edge[u].push_back(v); edge[v].push_back(u); } DFS(s,s); for(int i=1;i<=31;++i) for(int j=1;j<=n;++j) up[j][i]=up[up[j][i-1]][i-1]; sort(po.begin(),po.end()); int l=1; if (cnt[po[0].id]) ans=max(1ll,cnt[po[0].id]-1); for(int i=2;i<n;++i){ if (po[i].deep!=po[i-1].deep){ solve(l,i-1); l=i; } } solve(l,n-1); write(ans,'\n'); }
H. 纸牌游戏 (Nowcoder 5278 H)
题目大意
给定长度为的数字串,每个数字到,要求从中选取个数字组成一个非负整数,使得它可以被整除且最大。
解题思路
首先对数字串中的数字从大到小排序,然后设表示当前第个数,还可以取个数,当前选的数组成的数对取模为,后继是否存在可行方案(记忆化搜索,含义似乎难以描述qwq),转移就看这第个数选或不选两种方式。时间复杂度爆了空间和时间。
稍加思索会发现我们定义的状态里有好多***冗余重复 ***的状态,如,我们会考虑了多次选个或个的情况或个的情况,于是我们改变定义方式为为当前考虑选择数字(从9开始考虑),还有个可以选,对取模为后继是否存在可行方案,转移即可。时间复杂度。
特判全是且的情况。
多组数据,不能每组,于是玄学操作——自定义真假未定义。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N=1e5+8; char s[N]; int cc[10]; int tt=0; int dp[10][N][3]; int sum[10]; bool DFS(int num,int k,int mo,int cnt[]){ if (k==0) return mo==0; if (num<0) return false; if (k>sum[num]) return false; //可行性剪枝 if (dp[num][k][mo]>tt) return dp[num][k][mo]==tt+2; for(int i=min(cnt[num],k);i>=0;--i){ if (DFS(num-1,k-i,(mo+num*i)%3,cnt)){ cc[num]=i; dp[num][k][mo]=tt+2; return true; } } dp[num][k][mo]=tt+1; return false; } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++){ scanf("%s",s); int k; read(k); int cnt[10]={0}; int len=strlen(s); for(int i=0;i<len;++i) cnt[s[i]-'0']++; for(int i=0;i<10;++i) cc[i]=0; sum[0]=cnt[0]; for(int i=1;i<10;++i) sum[i]=cnt[i]+sum[i-1]; bool qwq=DFS(9,k,0,cnt); if (qwq&&(cc[0]!=k||k==1)){ char ans[k+1]={0}; int cr=0; for(int i=9;i>=0;--i){ while(cc[i]){ ans[cr++]=i+'0'; cc[i]--; } } printf("%s\n",ans); }else puts("-1"); tt+=3; } return 0; }
I. 古老的打字机 (Nowcoder 5278 I)
题目大意
给定个串以及对应的价值,现在会敲击打字机次,每次会等概率的敲击出一个小写字母或者退格键,退格键的作用是将当前得到的字符串的最后一个字母删除,当然如果当前是空串,则无事发生。问得到的字符串的价值的期望的倍是多少。
字符串的价值定义为:每个串在得到的串t中的出现次数与价值的乘积,即.
解题思路
根据期望的定义,答案就是,我们现在考虑 乱七八糟 怎么算。
乱七八糟 就是全部可能的字符串乘以对应的价值,直接计算的复杂度是。我们考虑如何合并。
我们先按长度进行分类,对于同一长度的字符串,它在的情况中出现的次数都是一样的。这一长度的总价值计算,从得到的字符串来考虑显然不现实,我们就经典转换成考虑每个对该长度总价值的贡献。
设当前考虑的字符串长度为,对于一个长度为小于等于的串,它对字符串长度为时的总价值贡献就为。
表示敲击次,得到串长度为的方案数。这个一开始预处理即可得到。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const LL mo=1e9+7; const int N=1e3+8; int n,m; int len[N]; LL val[N]; LL ans; LL dp[N][N]; LL p26[N]; char s[N]; LL qpower(LL a,LL b){ LL qwq=1; while(b){ if (b&1) qwq=qwq*a%mo; b>>=1; a=a*a%mo; } return qwq; } LL inv(LL x){ return qpower(x,mo-2); } int main(void) { read(n); read(m); for(int i=1;i<=n;++i){ scanf("%s",s); len[i]=strlen(s); read(val[i]); } p26[0]=1; for(int i=1;i<=1004;++i) p26[i]=p26[i-1]*26ll%mo; dp[0][0]=1; for(int i=1;i<=m;++i) for(int j=0;j<=i;++j){ if (j==0) dp[i][j]=(dp[i-1][j]+dp[i-1][j+1])%mo; else if (j>=i-1) dp[i][j]=dp[i-1][j-1]*26ll%mo; else dp[i][j]=(dp[i-1][j-1]*26ll%mo+dp[i-1][j+1])%mo; } LL tmp=1; LL qwq=1; for(int i=0;i<=m;++i){ for(int j=1;j<=n;++j){ if (len[j]>i) continue; ans=(ans+(i-len[j]+1ll)*p26[i-len[j]]%mo*val[j]%mo*dp[m][i]%mo*qwq%mo)%mo; } tmp=tmp*26%mo; qwq=inv(tmp); } write(ans,'\n'); return 0; }
J. 能到达吗 (Nowcoder 5278 J)
题目大意
给定一张的图,左上角坐标,右下角坐标,有个障碍物,第个障碍物坐标为。你可以在空地上上下左右走动,但不能跳出边界。问俩俩能到达的无序点对(即从一个点可以到达另外一个点)有多少。答案模。
解题思路
很显然,如果一个连通块有个点,那么这个连通块对答案的贡献就为,我们考虑如何求出每个连通块中的点数。
由于,而,我们用一根扫描线对这张图从上往下扫描,当前第行,在该行的障碍物把该行分成了若干条互不相交的线段,这些线段归属于哪个连通块我们可以用并查集维护。
然后考虑第行,在该行的障碍物也把该行分成了若干条互不相交的线段,现在我们对第行和第行的线段进行 合并 ,这里的 合并 指的是连通块的合并,即如果这两行中的两条线段是相交的,则它们应属于同一连通块,合并完后我们拿第行的线段与行的线段继续合并即可。合并的过程就是一个模拟的过程。
对于该行没有障碍物的,则视为一条线段,如果有连续若干行无障碍物,这我们可以把这若干行压成一行,视为一条线段,与前一行或后一行合并。
特殊处理无障碍物的情况。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const LL mo=1e9+7; const int N=5e6+8; LL n,m,k; int f[N],tot; LL cnt[N]; pair<int,int> po[N]; struct data{ int l,r,id; }; vector<data> seg[2]; int cur; LL ans; LL inv2; int findfa(int x){ if (f[x]==x) return x; else return f[x]=findfa(f[x]); } bool check(data & a,data & b){ return a.r>=b.l&&a.l<=b.r; } void unionn(){ if (seg[cur^1].empty()) return; int l=0; for(auto & i:seg[cur]){ while(true){ if (l<(int)seg[cur^1].size()&&check(i,seg[cur^1][l])){ int fa=findfa(i.id); int fb=findfa(seg[cur^1][l].id); if (fa!=fb){ f[fa]=fb; cnt[fb]=(cnt[fb]+cnt[fa])%mo; } ++l; }else{ if (l==(int)seg[cur^1].size()||seg[cur^1][l].l>i.r){ l=max(l-1,0); break; }else ++l; } } } } void work(int u,int d){ if (u>d) return; cur^=1; while(!seg[cur].empty()) seg[cur].pop_back(); ++tot; f[tot]=tot; cnt[tot]=(LL)(d-u+1ll)*m%mo; seg[cur].push_back({1,(int)m,tot}); unionn(); } void solve(int l,int r){ if (l>r) return; int la=0; cur^=1; while(!seg[cur].empty()) seg[cur].pop_back(); for(int i=l;i<=r;++i){ if (po[i].second-la>1){ ++tot; f[tot]=tot; cnt[tot]=po[i].second-la-1; seg[cur].push_back({la+1,po[i].second-1,tot}); } la=po[i].second; } if (m>la){ ++tot; f[tot]=tot; cnt[tot]=m-la; seg[cur].push_back({la+1,(int)m,tot}); } unionn(); } LL qpower(LL a,LL b){ LL qwq=1; while(b){ if (b&1) qwq=qwq*a%mo; b>>=1; a=a*a%mo; } return qwq; } LL inv(LL x){ return qpower(x,mo-2); } int main(void) { int kase; read(kase); inv2=inv(2); for (int ii = 1; ii <= kase; ii++){ tot=0; ans=0; while(!seg[0].empty()) seg[0].pop_back(); while(!seg[1].empty()) seg[1].pop_back(); read(n); read(m); read(k); if (k==0) ans=((n*m%mo)*((n*m-1ll)%mo)%mo*inv2%mo+n*m%mo)%mo; else{ for(int i=1;i<=k;++i) read(po[i].first),read(po[i].second); sort(po+1,po+1+k); int la=0; int l=1; work(1,po[1].first-1); la=po[1].first; for(int i=2;i<=k;++i){ if (po[i].first!=la){ solve(l,i-1); if (po[i].first-la>1) work(la+1,po[i].first-1); //有空行 l=i; la=po[i].first; } } solve(l,k); la=po[k].first; if (n>la){ work(la+1,n); } for(int i=1;i<=tot;++i){ if (f[i]==i){ ans=(ans+cnt[i]*(cnt[i]-1)%mo*inv2%mo+cnt[i])%mo; } } } write(ans,'\n'); } return 0; }
K. 迷宫 (Nowcoder 5278 K)
题目大意
图上一些地方有障碍物,有一个地方是起点,有一个地方是终点。现要求从起点移动到终点,一次操作可以上下左右移动一格,但不能超出边界。同时给定一个整数,可以使用一次技能,即从当前点移动到另一点,两点的切比雪夫(横坐标差与纵坐标差的最大值)距离不得大于,使用一次技能算一步操作。问从起点到终点的最小操作次数,并输出一种可行移动方案。
解题思路
切比雪夫距离对应的就是一个正方形的范围。
我们从起点开始BFS,再从终点开始BFS,记录每个点到起点和终点的距离。
然后再枚举一个边长为的正方形范围,设这个范围中到起点的最小值为和到终点的最小值,则在该范围使用技能后的最小操作次数就是,对所有范围取最小值即为答案。
至于维护二维正方形的最小值,用两次单调队列即可。先对行的每个数维护它右边个的最小值,再对每一列,用这个最小值,维护每行向下个数的最小值。
至于输出方案,再记录前驱和取得的最小值的位置吧。
特殊处理的情况。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N=2e3+8; const int dx[4]={0,0,1,-1}; const int dy[4]={-1,1,0,0}; struct data{ int val; int x,y; bool operator < (const data & a) const{ return val<a.val; } operator int(){ return val; } }st[N][N],en[N][N],ast,aen; char ma[N][N]; int n,m,d; int ans; int sx,sy,ex,ey; pair<int,int> pre_st[N][N],pre_en[N][N]; void BFS_st(){ st[sx][sy].val=0; pre_st[sx][sy]=make_pair(sx,sy); queue<pair<int,int>> team; team.push(make_pair(sx,sy)); while(!team.empty()){ auto u=team.front(); team.pop(); for(int i=0;i<4;++i){ auto v=make_pair(u.first+dx[i],u.second+dy[i]); if (ma[v.first][v.second]=='X') continue; if (st[v.first][v.second]>st[u.first][u.second]+1){ st[v.first][v.second].val=st[u.first][u.second].val+1; team.push(v); pre_st[v.first][v.second]=u; } } } } void BFS_en(){ en[ex][ey].val=0; pre_en[ex][ey]=make_pair(ex,ey); queue<pair<int,int>> team; team.push(make_pair(ex,ey)); while(!team.empty()){ auto u=team.front(); team.pop(); for(int i=0;i<4;++i){ auto v=make_pair(u.first+dx[i],u.second+dy[i]); if (ma[v.first][v.second]=='X') continue; if (en[v.first][v.second]>en[u.first][u.second]+1){ en[v.first][v.second].val=en[u.first][u.second].val+1; team.push(v); pre_en[v.first][v.second]=u; } } } } void pre_s(){ deque<data> team; for(int i=1;i<=n;++i){ for(int j=1;j<=min(m,d+1);++j){ while(!team.empty()&&team.back()>=st[i][j]) team.pop_back(); team.push_back(st[i][j]); } st[i][1]=team.front(); for(int j=min(m,d+1)+1;j<=m;++j){ while(!team.empty()&&team.front().y<j-d) team.pop_front(); while(!team.empty()&&team.back()>=st[i][j]) team.pop_back(); team.push_back(st[i][j]); st[i][j-d]=team.front(); } while(!team.empty()) team.pop_back(); } for(int i=1;i<=max(1,m-d);++i){ for(int j=1;j<=min(n,d+1);++j){ while(!team.empty()&&team.back()>=st[j][i]) team.pop_back(); team.push_back(st[j][i]); } st[1][i]=team.front(); for(int j=min(n,d+1)+1;j<=n;++j){ while(!team.empty()&&team.front().x<j-d) team.pop_front(); while(!team.empty()&&team.back()>=st[j][i]) team.pop_back(); team.push_back(st[j][i]); st[j-d][i]=team.front(); } while(!team.empty()) team.pop_back(); } } void pre_e(){ deque<data> team; for(int i=1;i<=n;++i){ for(int j=1;j<=min(m,d+1);++j){ while(!team.empty()&&team.back()>=en[i][j]) team.pop_back(); team.push_back(en[i][j]); } en[i][1]=team.front(); for(int j=min(m,d+1)+1;j<=m;++j){ while(!team.empty()&&team.front().y<j-d) team.pop_front(); while(!team.empty()&&team.back()>=en[i][j]) team.pop_back(); team.push_back(en[i][j]); en[i][j-d]=team.front(); } while(!team.empty()) team.pop_back(); } for(int i=1;i<=max(1,m-d);++i){ for(int j=1;j<=min(n,d+1);++j){ while(!team.empty()&&team.back()>=en[j][i]) team.pop_back(); team.push_back(en[j][i]); } en[1][i]=team.front(); for(int j=min(n,d+1)+1;j<=n;++j){ while(!team.empty()&&team.front().x<j-d) team.pop_front(); while(!team.empty()&&team.back()>=en[j][i]) team.pop_back(); team.push_back(en[j][i]); en[j-d][i]=team.front(); } while(!team.empty()) team.pop_back(); } } void print_st(int x,int y){ if (x==sx&&y==sy){ printf("%d %d\n",sx-1,sy-1); return; } print_st(pre_st[x][y].first,pre_st[x][y].second); printf("%d %d\n",x-1,y-1); } void print_en(int x,int y){ if (x!=ast.x||y!=ast.y) printf("%d %d\n",x-1,y-1); if (x==ex&&y==ey) return; int tmp=x; x=pre_en[tmp][y].first; y=pre_en[tmp][y].second; while(x!=ex||y!=ey){ printf("%d %d\n",x-1,y-1); tmp=x; x=pre_en[tmp][y].first; y=pre_en[tmp][y].second; } if (x!=ast.x||y!=ast.y) printf("%d %d\n",x-1,y-1); } int main(){ read(n); read(m); read(d); for(int i=1;i<=n;++i) scanf("%s",ma[i]+1); for(int i=1;i<=n;++i) ma[i][0]=ma[i][m+1]='X'; for(int i=1;i<=m;++i) ma[0][i]=ma[n+1][i]='X'; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){ if (ma[i][j]=='S'){ sx=i; sy=j;} if (ma[i][j]=='T'){ ex=i; ey=j;} st[i][j]=en[i][j]={1000000007,i,j}; } BFS_st(); BFS_en(); ans=1e9+7; if (d==0){ for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if (st[i][j]+en[i][j]<ans){ ans=st[i][j]+en[i][j]; ast=st[i][j]; aen=en[i][j]; } }else{ pre_s(); pre_e(); for(int i=1;i<=max(1,n-d);++i) for(int j=1;j<=max(1,m-d);++j) if (st[i][j]+en[i][j]+1<ans){ ans=st[i][j]+en[i][j]+1; ast=st[i][j]; aen=en[i][j]; } } if (ans==1000000007) ans=-1; write(ans,'\n'); if (ans!=-1){ print_st(ast.x,ast.y); print_en(aen.x,aen.y); } return 0; }
L. 动物森友会 (Nowcoder 5278 L)
题目大意
一周七天,一周的某些天可以执行特定的事件,一天最多执行次事件,同一个事件可以一天可以执行多次。
现在列出要执行的某些事件和且执行该事件的次数,问执行完这些事件所需的最小天数。
解题思路
很显然,当天数足够大时,一定能执行完所有事件。
天数对决策的可行具有单调性,于是我们二分天数,对于一个特定的天数,一周的某些天的执行次数是一定的,我们的任务就是分配这些执行次数,该去执行哪些事件,执行几次,这是带有反悔性质的抉择,跑一遍网络流即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N=2e4+8; const int INF=1e9+7; int head[N],nxt[N*2],to[N*2],team[N*2],dis[N*2]; LL b_flow[N*2],flow[N*2]; int n,e,st,en,num; LL sum; void add(int u, int v, int w) { num++; nxt[num] = head[u]; to[num] = v; flow[num] = w; head[u] = num; num++; nxt[num] = head[v]; to[num] = u; flow[num] = 0; head[v] = num; } bool BFS() { int l = 0, r = 1; team[1] = st; memset(dis, 0, sizeof(dis)); dis[st] = 1; while (l < r) { int u = team[++l]; for (int v, i = head[u]; i; i = nxt[i]) { v = to[i]; if (dis[v] == 0 && flow[i]) { dis[v] = dis[u] + 1; team[++r] = v; } } } if (dis[en]) return true; else return false; } LL DFS(int u, LL f) { if (u == en) return f; LL qwq = 0, tmp = 0; for (int v, i = head[u]; i; i = nxt[i]) { v = to[i]; if (dis[v] == dis[u] + 1 && flow[i]) { qwq = DFS(v, min(f - tmp, flow[i])); flow[i] -= qwq; flow[i ^ 1] += qwq; tmp += qwq; if (tmp == f) return tmp; } } return tmp; } bool check(int x){ for(int i=2;i<=num;++i) flow[i]=b_flow[i]; for(int i=head[st];i;i=nxt[i]) flow[i]=(LL)((x-1)/7+((x-1)%7>=to[i]-1))*(LL)e; LL cnt=0; while(BFS()){ cnt+=DFS(st,INF); } return cnt==sum; } int main(void) { read(n); read(e); num=1; st=0; sum=0; en=n+8; for(int i=1;i<=7;++i) add(st,i,0); for(int c,m,i=1;i<=n;++i){ read(c); read(m); sum+=c; add(i+7,en,c); for(int v,j=1;j<=m;++j){ read(v); add(v,i+7,INF); } } for(int i=2;i<=num;++i) b_flow[i]=flow[i]; int l=0,r=(sum+1)*8; int ans=0; while(l+1<r){ int mid=(l+r)>>1; if (check(mid)) ans=mid,r=mid; else l=mid; } printf("%d\n",ans); return 0; }
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/12865951.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步