zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试48]反思+题解

    状态很垃圾的一场考试。感觉“这么多分就够了”的心态很是在给自己拖后腿。

    打开题面,第一页赫然写着:$Claris' Contest$。

    吓得我差点手一抖关掉。不过后来想想似乎强到变态的人出的题都不是很不可做?大概是实力越强越能体会弱者的难处吧。

     看T1:woc 字符串?完蛋完蛋。

    T2:什么啊?图上乱搞?

    T3:最短路?边都建不出来。

    回去又读了一遍T1发现是sbDP,一眼切了开始码。结果死调不出来,考试开始25min的时候真的是有点慌,这么水的题别人估计都10min以内解决,我都调了快20min还是过不了手玩样例。好在用了35min调过了。

    T2无向图的限制让我很是没思路,于是先把T3暴力打了。打的时候想到可以拆点考虑每个二进制位,但是没想具体实现。

    回来搞T2发现$O(n^4)$的暴力很好打,淼之。

    然后就假装自己在想题实际上无所事事的过了1h……一直没想出T2其他的可行思路,主要是不知道无向图怎么传递信息(没有拓扑这种好东西)。

    之后突然想到可以把$O(n^4)$优化到$O(n^3)$,码了个对拍没出错。$210pts get!$

    优化到$n^3$之后发现这已经是优化枚举的瓶颈了,更加确定正解不是这个。想了一下$bitset$用于传递环信息但是好像不行。

    T3也没有想着拿下一档分,拆点的思路有点偏了。

    然而T2正解就是$bitset$优化三元环计数,T3就是拆点……而且70分很好拿……

    A.String Master

    无脑dp。LCS加一维、多一层循环枚举修改次数即可。复杂度$O(n^3)$。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    const int N=305;
    int n,K,dp[N][N][N];
    char a[N],b[N];
    int main()
    {
    	scanf("%d%d",&n,&K);
    	scanf("%s",a+1);scanf("%s",b+1);
    	int ans=0;
    	for(int k=0;k<=K;k++)
    	{
    		for(int i=1;i<=n;i++)
    		{
    			for(int j=1;j<=n;j++)
    			{
    				if(a[i]==b[j])dp[i][j][k]=max(dp[i-1][j-1][k]+1,dp[i][j][k]);
    				else if(k)
    					dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-1][k-1]+1);
    				ans=max(ans,dp[i][j][k]);
    			}
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    
    }
    

    B.Tourist Attractions

    最暴力的方法当然是四层循环枚举方案。如果记录一下每个点的度数,统计时直接用$deg[x]-1$减去三元环的情况就可以去掉一层循环。但显然我们不能用这样的方式再去一层了。

    那么可以只枚举四元环中间的两个点,把他们的度数-1再相乘后减去三元环的情况。怎么求三元环的情况数呢?用bitset维护每个点的联通集合,两个点的bitset与一下就是答案了。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<bitset>
    using namespace std;
    const int N=1505,M=2500000;
    typedef long long ll;
    int n;
    int to[M],head[N],nxt[M],deg[N],tot;
    ll ans;
    void add(int x,int y)
    {
        to[++tot]=y;
        nxt[tot]=head[x];
        head[x]=tot;
        deg[y]++;
    }
    char s[N];
    bitset<N> link[N];
    int main()
    {
        /*freopen("dt.in","r",stdin);
        freopen("my.out","w",stdout);*/
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s+1);
            for(int j=1;j<=n;j++)
            {
                int e=s[j]-'0';
                if(e)add(i,j),link[i][j]=1;
            }
        }
        for(int x=1;x<=n;x++)
        {
            for(int i=head[x];i;i=nxt[i])
            {
                int y=to[i];
                ans+=1LL*(deg[x]-1)*(deg[y]-1)-(link[x]&link[y]).count();
            }
        }
        cout<<ans<<endl;
        return 0;
    }
    

    C.Walk

    边权为1所以求最短路直接bfs就好了。把二进制数看作集合,那么题目要求的就是一个点要与$val$值是它$val$的子集的点连边。把所有$val$都看作点,对于每个点,让它的$val$对它连边。bfs入队的时候暴力枚举子集就可以让所有相连的点入队,由于每个点只有首次入队是有用的,所以可以把枚举过的子集记忆化一下减少重复操作。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    const int N=200005,M=300005;
    int n,m,w[N];
    int to[M],head[N],nxt[M],tot;
    vector<int> g[(1<<20)+5];
    void add(int x,int y)
    {
        to[++tot]=y;
        nxt[tot]=head[x];
        head[x]=tot;
    }
    int dis[N],vis[(1<<20)+5];
    queue<int> q;
    void relink(int val,int x,int pre)
    {
        if(vis[val])return ;
        vis[val]=1;
        for(int i=0;i<g[val].size();i++)
        {
            int y=g[val][i];
            if(dis[y]==-1)dis[y]=dis[x]+1,q.push(y);
        }
        for(int i=pre+1;i<=20;i++)
            if((val>>i)&1)relink(val^(1<<i),x,pre+1);
    }
    
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=n;i++)
            w[i]=read(),g[w[i]].push_back(i);
        for(int i=1;i<=m;i++)
        {
            int x=read(),y=read();
            add(x,y);
        }
        memset(dis,0xFF,sizeof(dis));
        q.push(1);dis[1]=0;
        while(!q.empty())
        {
            int x=q.front();q.pop();
            for(int i=head[x];i;i=nxt[i])
            {
                int y=to[i];
                if(dis[y]==-1)dis[y]=dis[x]+1,q.push(y);
            }
            relink(w[x],x,-1);
        }
        for(int i=1;i<=n;i++)
            printf("%d
    ",dis[i]);
        return 0;
    }
    
  • 相关阅读:
    力扣----4. 有效的括号(JavaScript, Java实现)
    力扣----3. 无重复字符的最长子串(JavaScript, Java实现)
    力扣----2. 两数相加(JavaScript, Java实现)
    力扣----1. 两数之和(JavaScript, Java实现)
    sql server实现copy data功能的存储过程(公共代码)
    inner join 与 left join 与 right join之间的区别
    redux
    Spring Boot-3 (@PathVariable和@RequestParam)
    小程序 wx.request
    小程序 -- ui布局
  • 原文地址:https://www.cnblogs.com/Rorschach-XR/p/11563850.html
Copyright © 2011-2022 走看看