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 赋值操作)

\[([i>1][j<len_a][a[i-1]=a[j+1]])dp[i-1][j+1][p][q]=1 \]

\[([p>1][q<len_b][b[p-1]=b[q+1]])dp[i][j][p-1][q+1]=1 \]

\[([i>1][q<len_b][a[i-1]=b[q+1]])dp[i-1][j][p][q+1]=1 \]

\[([p>1][j<len_a][a[j+1]=b[p-1]])dp[i][j+1][p-1][q]=1 \]

最后答案为 $$\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:


posted @ 2025-10-24 07:42  Wy_x  阅读(28)  评论(1)    收藏  举报