本文最后编辑于 前,其中的内容可能需要更新。
10.15打的组队赛,感觉自从使用了战术之后发挥稳定了不少,出了9/12题。
战术:Hile_Meow非必要不上机,Hile_Meow所读题意必让队友确认(经典wa签到+读歪题)
A. Micro Structure Thread
构造+MST。待补
B. Team
网络流。考虑通过源汇点的流量控制$m$个组合,然后$B$向$A$建边,$A$向$C$建边,流量为1,费用为预处理的$f(u,v)$,$A$拆点控制流量为1。
虽然是求最大权值,但是因为所有路径长度相同,我们可以令每条边的权值为$M-f(u,v)$,然后跑dijkstra费用流,或者直接边权取反上spfa费用流(可处理负权边)。
C. Function
数学。显然$f(x)\le x$,而且根据这个后缀积的不可构造性,猜测大概率会收敛到一个$f(x)=x$的值(如$k000001$或一个个位数),看榜上过的挺快就直接冲了一发记忆化+暴力,跑得飞快。
D. Fall Guys
签到。Boboge秒了。
E. Liner vectors
构造。首先显然$n=k$且$n\ne 1$时无解,然后考虑当$k\%2=0$时,每一维度形成的列向量异或为0,说明任意列向量都等于其他$n-1$个列向量的异或和,矩阵不满秩。
接下来就好构造了,前$k+1$个向量的前$k+1$位形成一个对角线为0的矩阵,后面的行直接对前面$n-k-1$位每行分配一个$1$,每行剩下的$1$全放在最低的$k-1$位。
赛中写的唯一一道题,还被n=1,k=1卡了一发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <bits/stdc++.h> using namespace std; typedef long long ll; int n,k; void run() { scanf("%d%d",&n,&k); if(n==1){ printf("1\n"); } else if(k%2==0||n==k){ printf("-1\n"); }else{ vector<ll> ve; ll tmp=1LL<<(k+1); tmp--; for(int i=0;i<k+1;i++){ ve.push_back(tmp^(1LL<<i)); } tmp>>=2; for(int i=k+1;i<n;i++){ ve.push_back(tmp^(1LL<<i)); } sort(ve.begin(),ve.end()); for(int i=1;i<=n;i++){ printf("%lld%c",ve[i-1],i==n?'\n':' '); } } }
int main() { int T = 1; scanf("%d", &T); while (T--) { run(); } return 0; }
|
F. Splendor
贪心+大模拟的防ak。貌似不太可做
G. Halli Galli
签到。Boboge秒了。
H. PepperLa’s String
贪心。既然要优先最短,其次字典序最小。
分两类位置:
- 使字符串长度变短:删单个字符,删两个字符之一,删$16^k$个连续字符一端
- 使字符串长度不变:删连续相同字符一端
有第一类优先删第一类,如果能让字典序变小则删第一个能让字典序变小的,否则删最后一个第一类位置。
如果没有第一类位置,则删第一个字符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6+7; const int MOD = 1e9+7; string s; int len[N]; string tohex(int x){ stringstream ss; ss<<hex<<x; string t; ss>>t; for(auto &i:t){ if(islower(i)){ i+='A'-'a'; } } return t; } void solve(){ int cnt=1; s.push_back('0'); vector<pair<int,int>> p; for(int i=1;i<s.size();i++){ if(s[i-1]!=s[i]){ p.push_back({s[i-1],cnt}); cnt=1; }else cnt++; } int flag=0,les=N,mor=-1; for(int i=0;i<p.size();i++){ if(p[i].second==1){ if(i+1==p.size()||p[i].first>p[i+1].first){ les=min(les,i); }else{ mor=i; } flag=1; }else if(p[i].second==2){ mor=i; flag=1; }else if(len[p[i].second-1]!=len[p[i].second]){ mor=i; flag=1; } } if(flag){ if(les<N){ p[les].second--; }else{ p[mor].second--; } }else{ p[0].second--; } for(auto i:p){ char ch=i.first; int cnt=i.second; if(!cnt)continue; cout<<ch; string tmp=tohex(cnt); if(tmp!="1")cout<<tmp; } cout<<'\n'; } int main(){ len[0]=1; for(int i=1;i<N;i++){ len[i]=len[i/16]+1; } ios::sync_with_stdio(false); while(cin>>s){ solve(); } }
|
I. PepperLa’s Cram School
图论,小坑。由于边长相同,答案即为$d[i][j]$中最小值出现次数$/2$(一条边贡献两次),注意最小边权不一定是$1$。
J. Color the blocks
签到。从$(x,y)$到$(x+1,y+2),(x-1,y+2),(x+3,y),(x-3,y)$建边,答案即为$2^{连通块数}$,显然$n\ge4$时答案为$4$,其他手算一下就好了。
K. PepperLa’s Boast
dp。考虑$f[i][j]$表示在$(i,j)$处的答案,则每次转移只会从左、上、左上移动过来或者从以$(i,j)$为右下角的一个$k\times k$的矩形转移而来,也就是说要$O(1)$求出左上矩形内的最大值,这就是二维单调队列裸题了。
二维单调队列:对每行和每列都开一个单调队列,先把答案更新到该列中,再用该列的答案更新该行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e3+7; const int MOD = 1e9+7; const ll INF = 0x3f3f3f3f3f3f3f3f; int n,m,k,u,a[N][N]; int dx[3]={1,0,1}; int dy[3]={0,1,1}; ll f[N][N]; deque<pair<ll,ll>> col[N],row[N]; bool ok(int x,int y){ return x>=1&&x<=n&&y>=1&&y<=m; } void initQ(int n,int m){ for(int i=1;i<=n;i++){ while(row[i].size()){ row[i].pop_back(); } } for(int i=1;i<=m;i++){ while(col[i].size()){ col[i].pop_back(); } } } void push(deque<pair<ll,ll>> &q,pair<ll,ll> x){ while(q.size()&&q.back().first<=x.first){ q.pop_back(); } q.push_back(x); } ll top(deque<pair<ll,ll>> &q,ll x){ while(q.size()&&q.front().second<=x)q.pop_front(); if(q.size())return q.front().first; return -1; } int main(){ ios::sync_with_stdio(false); while(cin>>n>>m>>k>>u){k++; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>a[i][j]; f[i][j]=-INF; } } initQ(n,m); f[1][1]=a[1][1]; push(col[1],{f[1][1],1}); push(row[1],{f[1][1],1}); for(int i=1;i<=n;i++){ for(int j=1+(i==1);j<=m;j++){ if(a[i][j]<=0){ f[i][j]=-1; }else{ for(int t=0;t<3;t++){ if(ok(i-dx[t],j-dy[t])&&f[i-dx[t]][j-dy[t]]>=0){ f[i][j]=max(f[i][j],f[i-dx[t]][j-dy[t]]+a[i][j]); } } ll t=max(top(row[i],j-k),top(col[j],i-k)); if(t>=0)f[i][j]=max(f[i][j],t+a[i][j]-u); } push(col[j],{f[i][j],i}); push(row[i],{top(col[j],i-k),j}); } } cout<<max(-1LL,f[n][m])<<"\n"; } }
|
L. PepperLa’s Express
搜索。首先遇到$\min$和$\max$来回套的题,第一反应必是二分答案。考虑二分了一个答案$mid$,可以先做一个bfs把所有已经和’@’距离在$mid$内的’*‘搞掉,然后考虑求出距离每个位置最远的’*‘,如果存在某个空格最远距离小于$mid$,说明可以把新的’@’放在这里使答案不大于$mid$。
赛中其实讨论了一个憨批做法,对于每一维来回扫一遍,从一维更新到三维,复杂度$O(8XYZ)$,但是写起来有点恶心,而且好像会爆空间,赛中没写完。
看出题人的方法有点牛,既然是找距离最远的点,那必然是$|x_i-x_j|+|y_i-y_j|+|z_i-z_j|$最大,我们可以把里面的正负号拆开,$i,j$各存$2^3$种,我们可以直接在为长为3的二进制互为补集的两种组合中求答案。
感觉这个拆绝对值符号的思路挺好,而且代码写起来也很简洁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 200+7; const int MOD = 1e9+7; const int INF = 10000; int h,n,m; char s[N][N][N]; int d[N][N][N]; int f[N][N][N]; int g[8]; int dx[6]={1,-1,0,0,0,0}; int dy[6]={0,0,1,-1,0,0}; int dz[6]={0,0,0,0,1,-1}; int sgn[2]={-1,1}; bool ok(int x,int y,int z){ return x>=1&&x<=n&&y>=1&&y<=m&&z>=1&&z<=h; } struct node{ int x,y,z; }; void bfs(){ queue<node> q; for(int k=1;k<=h;k++){ for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(!d[k][i][j]){ q.push({i,j,k}); } } } } while(q.size()){ node p=q.front(); q.pop(); for(int t=0;t<6;t++){ int x=p.x+dx[t]; int y=p.y+dy[t]; int z=p.z+dz[t]; if(ok(x,y,z)&&d[z][x][y]==-1){ d[z][x][y]=d[p.z][p.x][p.y]+1; q.push({x,y,z}); } } } } bool check(int mid){ for(int k=0;k<=h+1;k++){ for(int i=0;i<=n+1;i++){ for(int j=0;j<=m+1;j++){ f[k][i][j]=-INF; } } } memset(g,-0x3f,sizeof g); for(int k=1;k<=h;k++){ for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(s[k][i][j]=='*'&&d[k][i][j]>mid){ for(int t=0;t<8;t++){ g[t]=max(g[t],k*sgn[(t)&1]+i*sgn[(t>>1)&1]+j*sgn[(t>>2)&1]); } } } } } int cnt=0; for(int k=1;k<=h;k++){ for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(s[k][i][j]=='.'){ int res=0; cnt++; for(int t=0;t<8;t++){ res=max(res,g[t^7]+k*sgn[(t)&1]+i*sgn[(t>>1)&1]+j*sgn[(t>>2)&1]); } if(res<=mid){ return 1; } } } } } return !cnt; } int main(){ ios::sync_with_stdio(false); while(cin>>h>>n>>m){ for(int k=1;k<=h;k++){ for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>s[k][i][j]; d[k][i][j]=-1; if(s[k][i][j]=='@'){ d[k][i][j]=0; } } } } bfs(); int l=0,r=h+n+m,ans=0; while(l<=r){ int mid=l+r>>1; if(check(mid)){ ans=mid; r=mid-1; }else l=mid+1; } cout<<ans<<"\n"; } }
|