[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\n",dis[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/Rorschach-XR/p/11563850.html

时间: 2024-08-02 09:01:12

[CSP-S模拟测试48]反思+题解的相关文章

[CSP-S模拟测试50]反思+题解

??大部分人都觉得T3是道不可做题去刚T1T2了,于是我就侥幸苟到了前面? 这场考试比较成功的就是快速水掉了T1T2的部分分,1h拿到88分起码为之后硬肝T3上了保险(赛后发现就算T3爆零也能rank15?) 剩下也就没什么了……T3的分完全是时间堆出来的,还有运气成分.因为当时第一个A掉了二分答案专题的奶牛健美操那道题,所以看到直径下意识想维护子树最长链+次长链,而且要不是前面两道题都不会我才不敢写那个恶心至极的分类讨论换根QAQ.单就方法而言,我打的东西其实挺无脑的,并没有思考太多针对本题的

2019.9.20 csp-s模拟测试48 反思总结

头疼,不说废话了,祝大家rp++. T1: 暴力枚举,n3. 枚举两个串开始匹配的位置,每一次尽量修改. #include<iostream> #include<cstdio> using namespace std; int n,k,cnt,num,ans; char a[310],b[310]; int main() { scanf("%d%d",&n,&k); scanf("%s",a+1); scanf("%

2019.9.28 csp-s模拟测试54 反思总结

咕咕咕的冲动如此强烈x T1x: 看完题目想了想,感觉把gcd不为1的强行放在一组,看作一个连通块,最后考虑连通块之间的组合方式就可以了. 然后维护这个连通块可以写并查集可以连边跑dfs怎么着都行… 然而我在处理数字分解质因数这里T掉了,原因是一个很显然的优化写法我基本没怎么写过.线性筛的时候记录每个数是被哪个质数标记过的,分解一个数的时候直接处理记录下来的质数就可以. #include<iostream> #include<cstdio> #include<cmath>

2019.9.19 csp-s模拟测试47 反思总结

思路接近正解?都想到了?这都是借口呀. 没有用的,往前走吧. T1:Emotional Flutter 我的做法和题解不太一样,我把s放在最后考虑了. 因为出发以后步幅是一样的,所以每一个黑条可以ban掉一段出发点.把黑条的左右边界%k存成区间,每个黑条可以存一个或者两个区间[跨越k这个边界].然后像以前写区间覆盖的贪心一样按左端点排序,看看有没有长至少为s的空余. 代码: #include<iostream> #include<cstdio> #include<cstrin

csps模拟测试50反思

又考崩了,T1一眼秒掉错误思路,然后迅速码完,并码完错误暴力,对拍拍上,以为AC.T2想到了二维莫队,发现是子任务就没去打,一直在想别的,T3最后想到60分打法,没有打完,也没时间暴力,挂掉.T2还有一个读错题的锅,T了一个子任务. 考试一定要合理分配时间,确定自己算法的正确性,想到一个类似的算法要敢于去实现. T1 施工 单调栈优化dp 改变dp定义是优化dp的重要方式 dp[i]表示第i个位置不变的最优答案.枚举j转移,O(n^2),期望得分53 考虑优化,dp[i]只会由最多一个h比它大的

2019.9.26 csp-s模拟测试52 反思总结

刚刚写了一个小时的博客没了,浏览器自动刷新. 一!个!小!时! 鼠标键盘电脑哪个都不能摔,气死我了. 垃圾选手T1T2没思路,T3倒是想出来得比较早,靠T3撑着分数. 数据结构学傻选手,属实垃圾. T1平均数: 一个序列的所有数如果减去x,那么平均数也会减去x.可以二分这个x,统计序列里平均数小于0的序列的个数,含义为原序列平均数小于x的序列的个数.最后统计值小于k且最接近k的x就是所求答案. 序列的平均数小于0,那么序列的和也一定小于0.表现在前缀和上即为一个区间的sumr<suml-1,转化

2019.9.29 csp-s模拟测试55 反思总结

不咕咕咕是一种美德[大雾] 头一次体会到爆肝写题解??? 这次考试我们没赶上,是后来掐着时间每个人自己考的.我最后的分数能拿到152…熟悉的一题AC两题爆炸. 强烈吐槽出题人起名走心 T1联: 发现每一次加入一个区间的操作,只有区间的l或者r+1有可能成为答案.那么考虑能不能用这两个点代表一整个区间,维护全局最靠左的0在什么地方. 把每个操作的l和r+1都存下来,离散化,建一棵线段树.每一次区间操作都针对线段树上的a[l]-a[r+1]-1这部分(a[x]为x离散化以后的排序,即线段树里的位置)

2019.10.21 csp-s模拟测试81 反思总结

T1: 把每一行状压,按行DP.设fi,j,k,i表示第几行,j是当前行的1覆盖状态,k是当前行选择按钮的状态.转移的时候枚举j和k,再枚举下一层的按钮选择情况l.如果l和j可以全覆盖当前层则转移合法,根据下一层选择l状态的代价进行转移.预处理一行每一种选法i可以覆盖到的状态di,各行选择按钮状态i对应的代价dpi,以及每一行的初始状态bi.转移时下一层的覆盖情况就是k|dl|bi+1.初始化第一层是所有选法i对应的代价,即f1,d[i]|b[1],i=dp1,i. 整个DP过程的复杂度是O(3

2019.10.26 csp-s模拟测试88 反思总结

今天的主人公是什么? 60.1K!!!! 先扔代码再更新防止我等会儿一上头不打算写完题解 T1: #include<iostream> #include<cstdio> #include<algorithm> using namespace std; const double inf=214748364; int n,k; double a[100010],f[21][100010]; int main() { scanf("%d%d",&n