2022NOIP A层联测22
下发文件(密码为原 accoders 比赛密码)
这次比赛的难度不按顺序出牌,要记住在场上先通览一遍所有四个题再决定去怎么做. 这次最简单的题目是 T4,而最难的是 T1.
极源流体
暴力做法就是枚举横行走的步数,枚举纵列走的步数,判断是否合法,加上特判 35 分就到手了.
稍微优化一下,找到横行能走的最小步数,从这里开始枚举,直到枚举完或者时间到了 3 秒为止,然后暴力找到相应的纵列可走的最小步数,然后取一个最小的答案,79 分也就拿到了.
点击查看代码
#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define ll int
#define rg register
#define rll rg ll
#define pll pair<ll,ll>
#define maxn 730
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
rg bool f=0;rll x=0;rg char ch=getchar();while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
ll dx[8][2]={ { 0,1 },{ 0,-1 },{ 1,0 },{ -1,0 },{ 1,1 },{ 1,-1 },{ -1,1 },{ -1,-1 } };
ll n,m,ls,mx,tot,ans;
ll mp[maxn][maxn],mp1[maxn][maxn];
ll fl[maxn*maxn],fl2[maxn][maxn];
char s[maxn];
queue<pll> q;
inline void bfs()
{
for(rll i=1;i<=n;i++) for(rll j=1;j<=m;j++) if(mp1[i][j]==1)
{
mp1[i][j]=++tot; q.push((pll) { i,j }); while(!q.empty())
{
rll x=q.front().first,y=q.front().second; q.pop();
// cout<<x<<' '<<y<<' '<<mp1[x][y]<<endl;
for(rll i=0;i<8;i++)
{
rll nx=x+dx[i][0],ny=y+dx[i][1]; if((!nx)||nx>n||(!ny)||ny>m) continue;
if(mp1[nx][ny]==1) mp1[nx][ny]=tot,q.push((pll) { nx,ny });// ,cout<<'*'<<nx<<' '<<ny<<' '<<mp1[nx][ny]<<endl;
}
}
}
}
inline ll chk(rll x)
{
memset(fl2,0,sizeof(fl2));memcpy(mp1,mp,sizeof(mp));rll ans=0;
for(rll i=1;i<=n;i++) for(rll j=m;j;j--) if(mp[i][j])
for(rll k=j+1;k<=min(j+x,m);k++) { if(mp1[i][k]) break; mp1[i][k]=1; }
for(rll i=1;i<=n;i++) for(rll j=1;j<=m;j++) fl2[i][j]=mp1[i][j];// rll ttt=0;
while(1)
{
rll ls=ans;
memset(fl,-1,sizeof(fl));tot=1;bfs();if(tot==2) break;
// for(rll i=1;i<=n;i++) { for(rll j=1;j<=m;j++) write(mp1[i][j]),put_;putn; }// ttt++;if(ttt>2) break;
for(rll i=1;i<=m;i++)
{
for(rll j=1;j<=n;j++) if(fl2[j][i])
{
rll t=0x3f3f3f3f; for(rll k=j-1;k;k--) if((fl2[k][i]&&mp1[k][i]!=mp1[j][i])||(fl2[k][i-1]&&mp1[k][i-1]!=mp1[j][i])||(fl2[k][i+1]&&mp1[k][i+1]!=mp1[j][i])) { t=min(t,j-k-1); break; }
for(rll k=j+1;k<=n;k++) if((fl2[k][i]&&mp1[k][i]!=mp1[j][i])||(fl2[k][i-1]&&mp1[k][i-1]!=mp1[j][i])||(fl2[k][i+1]&&mp1[k][i+1]!=mp1[j][i])) { t=min(t,k-j-1); break; }
// cout<<t<<endl;
if(t==0x3f3f3f3f) continue; if(!~fl[mp1[j][i]]) fl[mp1[j][i]]=t; else fl[mp1[j][i]]=min(fl[mp1[j][i]],t);
}
}
for(rll i=2;i<=tot;i++) if(~fl[i]) ans=max(ans,fl[i]); // ,write(fl[i]),put_;putn;
// if(ans<=ls) ans++;
for(rll i=1;i<=n;i++) for(rll j=1;j<=m;j++) mp1[i][j]=fl2[i][j];
for(rll i=1;i<=m;i++) for(rll j=n;j;j--) if(fl2[j][i])
for(rll k=j+1;k<=min(j+ans,n);k++) { mp1[k][i]=1; }
// cout<<ans<<endl;
}
// write(x);putn; write(ans+x);putn;
// cout<<x<<' '<<ans<<endl;
return ans+x;
}
int main()
{
freopen("fluid.in","r",stdin); freopen("fluid.out","w",stdout);
rll __tme=clock();
// mp[1][1]=1;memcpy(mp1,mp,sizeof(mp));write(mp1[1][1]);
n=read();m=read();
for(rll i=1;i<=n;i++) { scanf("%s",s+1); for(rll j=1;j<=m;j++) mp[i][j]=s[j]^'0'; }
for(rll j=1;j<=m;j++) for(rll i=1;i<=n;i++) if(mp[i][j]) { if(ls) mx=max(mx,j-ls-1); ls=j; break; }
rll l=mx,r=m;// cout<<'*'<<mx<<endl;
// while(l<r) { rll mid=(l+r)>>1,cl=chk(mid),cr=chk(mid+1); if(cl<cr) ans=cl,r=mid; else ans=cr,l=mid+1; }
ans=0x3f3f3f3f; for(rll i=l;i<=r&&clock()-__tme<=2700000;i++) ans=min(ans,chk(i));// ,write(ans);
write(ans);
return 0;
}
再重新审视一遍这道题,还是去枚举对应的行,求最小的纵列可走的步数. 对于这个暴力找,肯定是可以把它再优化一下的. 仔细思考,发现随着行走的步数的增加,列走的最小步数是单调不升的,既然不能整个二分就把列走的步数的查询搞个类似双指针的东西. 先正着反着跑两遍找到行的可行步数,然后再把它排个序,从小到大找一遍用一个并查集维护一下连通块找到纵的可以走的最少步数(优化暴力),答案取最小值.
口胡的,没看懂可以看看实现,其实不难理解.
点击查看代码
#include<bits/stdc++.h>
#define ll int
#define rg register
#define rll rg ll
#define pll pair<ll,ll>
#define maxn 730
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
rg bool f=0;rll x=0;rg char ch=getchar();while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
ll dx[8][2]={ { 0,1 },{ 0,-1 },{ 1,0 },{ -1,0 },{ 1,1 },{ 1,-1 },{ -1,1 },{ -1,-1 } };
struct node
{
ll x,y,a,b;
inline friend bool operator<(rg node a,rg node b) { return a.a<b.a; }
};
ll n,m,ans,tot=1,mp[maxn][maxn],pos[maxn],v[maxn],f[maxn];
char s[maxn];
queue<pll> q;
vector<node> t;
vector<pll> g[maxn];
inline ll find(rll x) { if(x^f[x]) f[x]=find(f[x]); return f[x]; }
inline void bfs()
{
for(rll i=1;i<=n;i++) for(rll j=1;j<=m;j++) if(mp[i][j]==1)
{
mp[i][j]=++tot; q.push((pll) { i,j }); while(!q.empty())
{
rll x=q.front().first,y=q.front().second; q.pop();
for(rll i=0;i<8;i++)
{
rll nx=x+dx[i][0],ny=y+dx[i][1]; if((!nx)||nx>n||(!ny)||ny>m) continue;
if(mp[nx][ny]==1) mp[nx][ny]=tot,q.push((pll) { nx,ny });
}
}
}
}
int main()
{
freopen("fluid.in","r",stdin); freopen("fluid.out","w",stdout);
// rll __tme=clock();
// mp[1][1]=1;memcpy(mp,mp,sizeof(mp));write(mp[1][1]);
n=read();m=read(); ans=m+n-2;
for(rll i=1;i<=n;i++) { scanf("%s",s+1); for(rll j=1;j<=m;j++) mp[i][j]=s[j]^'0'; }
// 找联通块
bfs(); tot--; for(rll i=1;i<=n;i++) for(rll j=1;j<=m;j++) if(mp[i][j]) mp[i][j]--;
// 横行处理
for(rll j=1;j<=m;j++) for(rll i=1;i<=n;i++) if(mp[i][j])
{
if(pos[i]&&(pos[i]^mp[i][j])) t.push_back((node) { pos[i],mp[i][j],0,max(0,j-v[i]-1) });
for(rll k=i-1,mx=v[i];k;k--) if(v[k]&&v[k]>mx) { if(v[k]^mp[i][j]) t.push_back((node) { pos[k],mp[i][j],i-k-1,max(0,j-v[k]-1) }); mx=v[k]; }
pos[i]=mp[i][j],v[i]=j;
}
memset(pos,0,sizeof(pos));memset(v,0,sizeof(v));
for(rll j=1;j<=m;j++) for(rll i=n;i;i--) if(mp[i][j])
{
for(rll k=i+1,mx=v[i];k<=n;k++) if(v[k]&&v[k]>mx) { if(v[k]^mp[i][j]) t.push_back((node) { pos[k],mp[i][j],k-i-1,max(0,j-v[k]-1) }); mx=v[k]; }
pos[i]=mp[i][j],v[i]=j;
}
sort(t.begin(),t.end());
// 纵列处理
for(rll i=0,cnt,mx;i<t.size();i++)
{
g[t[i].b].push_back((pll) { t[i].x,t[i].y }); if(i<t.size()-1&&t[i].a==t[i+1].a) continue;
cnt=mx=0; for(rll i=1;i<=tot;i++) f[i]=i;
for(rll j=0;j<=m;j++)
{
for(rll k=0;k<g[j].size();k++) if(find(g[j][k].first)^find(g[j][k].second))
f[find(g[j][k].second)]=g[j][k].first,cnt++,mx=j;
if(cnt==tot-1) { ans=min(ans,t[i].a+mx); break; }
}
}
write(ans);
return 0;
}
正解是 LCT,不会. 但是上面的算法已经足以通过这道题的数据范围(O(n3)).
等差数列
暴力还是很好说,枚举首项和它后边的每一个数去计算公差,然后扫一遍序列暴力判断是否合法并累加答案,能拿 70 分(暴力分给得很足了).
发现公差的范围和 n 的范围是一样的,所以可以只去枚举公差(上界是 ),然后对于数列中的每一个数,都有一个对应的值. 扫一遍整个序列找到出现次数最多的这个值,把它作为首项,计算一下答案.
点击查看代码
#include<bits/stdc++.h>
#define ll int
#define rg register
#define rll rg ll
#define maxn 271829
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
rg bool f=0;rll x=0;rg char ch=getchar();while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
ll n,w,mx,id,ans=0x3f3f3f3f;
ll a[maxn],cnt[maxn];
bool flag;
int main()
{
freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout);
n=read();w=read(); for(rll i=1;i<=n;i++) a[i]=read(); ans=n;
for(rll i=1;i<=n;i++) if(a[i]^1) { flag=1;break; } if((!flag)||n==1) { puts("0");return 0; }
for(rll i=0;i<=(w-1)/(n-1);i++)
{
memset(cnt,0,sizeof(cnt));mx=id=0;
for(rll j=2,t;j<=n;j++)
{
t=a[j]-(j-1)*i; if(t<=0) continue; cnt[t]++;if(cnt[t]>mx) mx=cnt[t],id=t;
}
ans=min(ans,n-mx-1+(bool)(a[1]^id));
}
write(ans);
return 0;
}
送给好友的礼物
树形 dp.
转化一下题目,对于一个有草莓的点(关键点),如果其子树内存在关键点,那么它一定会被经过,就不需要考虑它;反之如果不存在,那么就只需要考虑它,就不需要去管它的子树了.
按照这种思路,最终由关键点构成的一棵树一定是一棵只有所有叶子节点是关键点的树.
那么要求的其实就是,把这棵树划分为两个集合,最小的两个联通块大小的 2 倍.
然后就可以进行 dp 了.
设这个联通块的大小为 w(S). 那么手模不难发现,设整个集合中的点按 dfs 序排序后的顺序为 dfn(i),这个 w(S) 其实就等于 Σi dep[dfn(i)] - dep[lca(dfn(i) , dfn(i-1)](i = 1 时后面不减那一项).
那么可以设 dp[i][j][k] 表示到了第 i 个关键点,前一个在第一个集合/第二个集合的关键点为 j(因为另一个肯定是 i - 1),第一个集合的 w(S) 大小为 k 时第二个集合的 w(S) 的最小值.
转移分两个,设 leaf[i] 为第 i 个叶子节点,那么有:
-
dp[i+1][i][dp[i][j][k]+dis(leaf[j],leaf[i+1])]=min{k};
-
dp[i+1][j][k+dis(leaf[i],leaf[i+1])]=min{dp[i][j][k]};.
实现时需要注意,dp 数组开大了会 MLE,由于数据范围很小,因此采用 short 存储.
点击查看代码
#include<bits/stdc++.h>
#define ll int
#define rg register
#define rll rg ll
#define maxn 451
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
rg bool f=0;rll x=0;rg char ch=getchar();while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
ll n,k,a[maxn],ans=0x3f3f;
vector<ll> g[maxn],leaf;
ll dfn[maxn],sz[maxn],tot;
short dis[maxn][maxn],dp[maxn][maxn][maxn<<1];
bool fl[maxn];
inline bool cmp(rll x,rll y) { return dfn[x]<dfn[y]; }
inline void dfs1(rll x,rll fa,rll rt)
{
for(rll i=0;i<g[x].size();i++) { rll to=g[x][i]; if(to==fa) continue; dis[rt][to]=dis[rt][x]+1; dfs1(to,x,rt); }
}
inline void dfs2(rll x,rll fa)
{
dfn[x]=++tot;sz[x]=fl[x];
for(rll i=0;i<g[x].size();i++) { rll to=g[x][i]; if(to==fa) continue; dfs2(to,x); sz[x]+=sz[to]; }
if(fl[x]&&sz[x]==1) leaf.emplace_back(x);
}
int main()
{
freopen("gift.in","r",stdin); freopen("gift.out","w",stdout);
// memset(dp,0x3f,sizeof(dp));write(dp[0][0][0]);
n=read();k=read(); for(rll i=1,u,v;i<n;i++) u=read(),v=read(),g[u].emplace_back(v),g[v].emplace_back(u);
for(rll i=1;i<=k;i++) fl[a[i]=read()]=1;
for(rll i=1;i<=n;i++) dfs1(i,0,i); leaf.emplace_back(1); dfs2(1,0); sort(leaf.begin(),leaf.end(),cmp);
assert(leaf.size()^1); memset(dp,0x3f,sizeof(dp)); dp[1][0][dis[1][leaf[1]]]=0;
for(rll i=1;i<leaf.size()-1;i++) for(rll j=0;j<i;j++) for(rll k=0;k<=n<<1;k++) if(dp[i][j][k]^0x3f3f)
dp[i+1][i][dp[i][j][k]+dis[leaf[j]][leaf[i+1]]]=min(dp[i+1][i][dp[i][j][k]+dis[leaf[j]][leaf[i+1]]],(short)k),
dp[i+1][j][k+dis[leaf[i]][leaf[i+1]]]=min(dp[i+1][j][k+dis[leaf[i]][leaf[i+1]]],dp[i][j][k]);
for(rll i=0;i<leaf.size()-1;i++) for(rll j=0;j<=min(ans,n<<1);j++)
ans=min(ans,max(j+dis[leaf.back()][1],(ll)dp[leaf.size()-1][i][j]+dis[leaf[i]][1]));
write(ans);
return 0;
}
非常困难的压轴题
因为它是压轴题,所以我决定先切掉它
倒着找,先找所有 S 做个后缀和,然后找 W 乘上 S 的情况也做个后缀和,最后找 L乘上 W 的情况统计答案.
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 142860
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
rg bool f=0;rll x=0;rg char ch=getchar();while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
ll n,ans;
char s[maxn];
ll sumw[maxn],sums[maxn];
int main()
{
freopen("lws.in","r",stdin); freopen("lws.out","w",stdout);
scanf("%s",s+1);n=strlen(s+1); for(rll i=n;i;i--) sums[i]=sums[i+1]+(s[i]=='S');
for(rll i=n;i;i--) sumw[i]=sumw[i+1]+(s[i]=='W')*sums[i+1];
for(rll i=n;i;i--) ans+=(s[i]=='L')*sumw[i+1]; write(ans);
return 0;
}
--END--

浙公网安备 33010602011771号
我的博客: 𝟷𝙻𝚒𝚞
本文链接: https://www.cnblogs.com/1Liu/p/16866973.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!