CSP-S模拟37(全真 1)
!CSP-S 全真模拟 1
流程:
\(8:00 \to 8:20\):浏览题目,了解大致难度。
\(8:20 \to 9:20\):发现 T3 很水,过 T3 大样例。
\(9:20 \to 11:00\):找 T1 规律,发现 T1 性质,过 T1 大样例。
\(11:00 \to 11:45\):拼包。
\(11:45 \to 11:50\):检查文件。
\(11:50 \to 12:00\):万 ys。 \(\color{white}{\text{原计划打快}}\)
不足:没拼 T2。
A. 回文 (string):
noi 大纲【区间 dp】【多维 dp】
比较非常规的一道题。
设 \(dp[i][j][p][q]\) 表示串 \(a\) 选区间 \([i,j]\),串 \(b\) 选区间 \([p,q]\) 时,是否可以构成回文。
将 a 串和 b 串的端点字符分别设为 '!''@''#''$' 防止出锅。
考虑使用主动转移的区间 dp 以减少分讨。
初始化单个字符为回文,其余直接主动转移。
就是 \(dp[i][i][j][j-1]=1,dp[i][i-1][j][j]=1\)。
假设现在枚举到 \(dp[i][j][p][q]\)。
四种情况:(dp 左侧为艾弗森括号,若为真时执行后续 dp 赋值操作)
最后答案为 $$\max((j-i+1+q-p+1)[dp[i][j][p][q]=1])$$。
注意初始化时应初始化 \([0 ~ len+1]\)。否则有一组 Hack:
in:
1
bbabdcdd
a
out:
5
坏了 Hacker 杀疯了!!!场上代码几乎无一幸免!
我的死因是转移时右界应到 \(len+1\) 而不是 \(len\)。
Code:
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,c=getchar(),f=0;
for(;c<'0'||c>'9';f=c=='-',c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=55;
char a[55],b[55];
bool dp[N][N][N][N];
void solve()
{
memset(dp,0,sizeof(dp));
scanf("%s%s",a+1,b+1);
int la=strlen(a+1),lb=strlen(b+1);
a[0]='!';
b[0]='@';
a[la+1]='#';
b[lb+1]='$';
a[la+2]='%';
b[lb+2]='^';
a[la+3]='&';
b[lb+3]='*';
int ans=1;
for(int i=0;i<=la;i++)
for(int j=1;j<=lb+1;j++)
dp[i][i][j][j-1]=1;
for(int i=0;i<=lb;i++)
for(int j=1;j<=la+1;j++)
dp[j][j-1][i][i]=1;
for(int i=1;i<=la;i++)
for(int j=1;j<=lb;j++)
if(a[i]==b[j]) dp[i][i][j][j]=1;
for(int i=0;i<=la;i++)
if(a[i]==a[i+1])
{
for(int j=1;j<=lb+1;j++)
dp[i][i+1][j][j-1]=1;
ans=2;
}
for(int i=0;i<=lb;i++)
if(b[i]==b[i+1])
{
for(int j=1;j<=la+1;j++)
dp[j][j-1][i][i+1]=1;
ans=2;
}
for(int lena=0;lena<=la+1;lena++)
{
for(int i=1;i<=la-lena+2;i++)
{
int j=i+lena-1;
for(int lenb=0;lenb<=lb+1;lenb++)
{
for(int p=1;p<=lb-lenb+2;p++)
{
int q=p+lenb-1;
// cout<<i<<" "<<j<<" "<<p<<" "<<q<<" dp="<<dp[i][j][p][q]<<"\n";
if(dp[i][j][p][q])
{
ans=max(ans,lena+lenb);
if(i>=1&&j<=la&&a[i-1]==a[j+1]) dp[i-1][j+1][p][q]=1;
if(p>=1&&q<=lb&&b[p-1]==b[q+1]) dp[i][j][p-1][q+1]=1;
if(i>=1&&q<=lb&&a[i-1]==b[q+1]) dp[i-1][j][p][q+1]=1;
if(p>=1&&j<=la&&b[p-1]==a[j+1]) dp[i][j+1][p-1][q]=1;
}
}
}
}
}
// cout<<dp[5][4][2][3]<<"\n";
cout<<ans<<"\n";
}
signed main()
{
freopen("aa.in","r",stdin);
freopen("aa.out","w",stdout);
int T;
cin>>T;
while(T--) solve();
return 0;
}
B. 数排列 (perm):

Code:
#include<bits/stdc++.h>
#define int long long
const int mod=1e9+7;
using namespace std;
inline int read()
{
int x=0,c=getchar(),f=0;
for(;c<'0'||c>'9';f=c=='-',c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
int n;
int a[1<<20];
int f[2005][2005];
signed main()
{
freopen("perm.in","r",stdin);
freopen("perm.out","w",stdout);
n=read();
if(n<=1) { cout<<1<<endl; return 0; }
for(int i=1;i<n;i++) a[i]=read();
f[0][1]=1;
for(int i=1;i<n;i++)
{
if(a[i]==1)
{
for(int j=2;j<=i+1;j++)
{
f[i][j]=f[i][j-1]+f[i-1][j-1];
f[i][j]%=mod;
}
}
else
{
for(int j=i;j>=1;j--)
{
f[i][j]=f[i][j+1]+f[i-1][j];
f[i][j]%=mod;
}
}
}
int ans=0;
for(int j=1;j<=n;j++)
{
ans+=f[n-1][j];
ans%=mod;
}
cout<<ans;
return 0;
}
C. 树上的背包 (knapsack):
发现建树操作建出来的每个点 \(i\) 的树高就是 \(\log_2{i}\le 17\)。
考虑直接暴力折半搜索。
设 \(f(i)=(i+1)\times 2^i\) 为搜索 \(i\) 个点并将结果排序之后的总时间复杂度。
复杂度 \(O(n \times (f(8) + f(9)))\) 直接爆炸(排序复杂度占大头)。
考虑优化。
可以先预处理一部分点的折半搜索的结果。
假设我们将深度为 \(B\) 的点到根的搜索结果求出。
复杂度变为 \(O(2^B times f(B) + n\times f(\max(B-1,17-B)) )\)。实际取 \(B=10\) 时最优。
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
int x=0,c=getchar(),f=0;
for(;c<'0'||c>'9';f=c=='-',c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=1e5+5;
int n;
int fa[N];
int dep[N];
int id[N];
int v[N],w[N];
struct Node{
int val,cost;
};
bool cmp(Node x,Node y) { return x.cost<y.cost||(x.cost==y.cost&&x.val>y.val); }
vector<Node> nw,nw2;
const int limit=10;
vector<Node> vec[(1<<limit)+limit];
void dfs1(int p,int val,int cost)
{
if(!p) { nw.emplace_back(Node{val,cost}); return; }
dfs1(fa[p],val,cost);
dfs1(fa[p],val+v[p],cost+w[p]);
}
void dfs2(int p,int minn,int val,int cost)
{
if(dep[p]<minn) { nw2.emplace_back(Node{val,cost}); return; }
dfs2(fa[p],minn,val,cost);
dfs2(fa[p],minn,val+v[p],cost+w[p]);
}
void solve(int p,int id)
{
dfs1(p,0,0);
sort(nw.begin(),nw.end(),cmp);
swap(nw,vec[id]);
}
signed main()
{
freopen("knapsack.in","r",stdin);
freopen("knapsack.out","w",stdout);
n=read();
dep[1]=1;
for(int i=2;i<=n;i++)
{
dep[i]=dep[i>>1]+1;
fa[i]=(i>>1);
}
for(int i=1;i<=n;i++)
{
v[i]=read();
w[i]=read();
}
int tot=0;
// nw.reserve(1<<limit);
for(int i=1;i<=n;i++)
{
if(dep[i]==limit)
{
id[i]=++tot;
// v[id[i]].reserve(1<<limit);
solve(i,id[i]);
// cout<<"i="<<i<<" id="<<id[i]<<"\n";
// for(Node a:vec[id[i]])
// {
// cout<<"cost="<<a.cost<<" val="<<a.val<<"\n";
// }
// cout<<"\n";
}
}
// return 0;
// return 0;
int Q=read();
while(Q--)
{
int u=read(),L=read();
// nw.clear();
// dfs1(u,0,0);
// {
// int ans=0;
// for(const Node &i:nw)
// {
// if(i.cost<=L) ans=max(ans,i.val);
// // else break;
// }
// write(ans);
// putchar('\n');
// continue;
// }
if(dep[u]==limit)
{
int ans=0;
for(const Node &i:vec[id[u]])
{
if(i.cost<=L) ans=max(ans,i.val);
// else break;
}
write(ans);
putchar('\n');
continue;
}
nw2.clear();
nw.clear();
int faa=u;
if(dep[u]<limit)
{
for(int i=1;i<=((dep[u]-1)>>1);i++) faa=fa[u];
dfs1(fa[faa],0,0);
dfs2(u,dep[faa],0,0);
sort(nw.begin(),nw.end(),cmp);
sort(nw2.begin(),nw2.end(),cmp);
}
else
{
for(int i=limit+1;i<=dep[u];i++) faa=fa[faa];
nw2.clear();
dfs2(u,limit+1,0,0);
sort(nw2.begin(),nw2.end(),cmp);
swap(vec[id[faa]],nw);
}
int ans=0,ma1=0;
int pos=0;
int len=nw.size();
for(int i=nw2.size()-1;i>=0;i--)
{
if(nw2[i].cost>L) continue;
while(pos<len&&nw[pos].cost+nw2[i].cost<=L)
{
ma1=max(ma1,nw[pos].val);
pos++;
}
ans=max(ans,ma1+nw2[i].val);
}
write(ans);
putchar('\n');
if(dep[u]>limit) swap(vec[id[faa]],nw);
}
// cout<<dep[n]<<"\n";
return 0;
}
D. 开会 (meeting):
Code:
以下是博客签名,正文无关
本文来自博客园,作者:Wy_x,转载请在文首注明原文链接:https://www.cnblogs.com/Wy-x/p/19161989
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC-BY-NC-SA 4.0 协议)进行许可。

浙公网安备 33010602011771号