10.4 代码源 2024 CSP-S 模拟赛 Day 9

省流:\(100+0+0+0=100\)

简称:唐诗

T1

先写了个暴力,然后在想怎么优化,然后想了个区间 DP 但是写的时候又不会了……

然后发现如果这一块数的二进制每一位都有一个数的这一位为 \(0\) 或者都相同,那么这些数合并起来一定最优,然后双指针搞,复杂度 \(O(30n)\)。(这么绕口)

赛后听别人说有规律:

所有数的与的和就是答案要求的数;

然后比一下就行了,复杂度 \(O(n)\)

想想发现很对,跟我的有异曲同工之妙。

核心代码
fd(k,0,30)
{
    f[n+1][k][0]=f[n+1][k][1]=0;
    bd(i,n,1)
    {
        f[i][k][0]=f[i+1][k][0];
        f[i][k][1]=f[i+1][k][1];
        f[i][k][(a[i].v>>k)&1]++;
    }
    can[k]=(f[1][k][0]==0||f[1][k][1]==0);
	flag&=can[k];
}
int l=n,r=n;
while(l<=r&&l>0)
{
    bool fl=1;
    fd(k,0,30)
    {
        if(can[k]) continue;
        if(f[l][k][0]-f[r+1][k][0]<=0)
            {fl=0;break;}
    }
    if(l==1)
    {
        if(fl) ans+=r-l;
        else ans+=r-l+1;
        break;
    }
	if(fl) ans+=r-l,r=l-1,--l;
	else --l;
}
if(flag) ans=0;

T2

一眼发现一整行/列都一样肯定最后涂的,然后……

二眼暴力,因为 \(n,m\le 1000\),然后懒得写看 T3 了。

\(70\) 分是把一整行/列都一样的直接删掉,然后贪一下就行了。

然后我们发现,设图形中有 \(k\) 列/行完全相同,答案就是 \(n+m-k\)

为什么呢?

因为最大次数为 \(n+m\)

然后如果有 \(k\) 列/行完全相同的话,就说明对这 \(k\) 行/列的操作无效,所以可以不做这 \(k\) 次操作。

然后可以 Hash 求相同行/列个数,然后对找到的 \(k\) 取个 \(\max\) 就行了。

然后判无解是找类似以下的情况:

...R...B...
...........
...B...R...
核心代码

inline void build(int x,int y,int op)
{
	v[x][op].push_back(y);
	v[y][op].push_back(x);
}

void dfs(int p,int op,int fa)
{
	if(vis[p][op]) {can=0;return;}
	vis[p][op]=1;
	for(auto i:v[p][op])
	{
		if(i==fa) continue;
		dfs(i,!op,p);
	}
	vis[p][op]=0;
	v[p][op].clear();
}

bool check()
{
	id=0;
	fd(i,1,n) d1[i]=++id;fd(i,1,m) d2[i]=++id;
	fd(i,1,id) v[i][0].clear(),v[i][1].clear();
	fd(i,1,n) fd(j,1,m) build(d1[i],d2[j],a[i][j]);
	can=1;
	fd(i,1,id)
	{
		dfs(i,0,0);dfs(i,1,0);
		if(!can) break;
	}
	return can;
}

inline int r64()
{
	return (rand()^rand())*rand()%mod;
}

inline int H(int x)
{
	x^=x<<11,x^=x>>45;
	x^=x<<14,x^=x>>19;
	return x;
}

void Main()
{
	ans=0;
	
	if(!check())
	{
		cout<<-1<<endl;
		return;
	}
	
	fd(i,1,n)
	{
		int hs=st;
		fd(j,1,m) hs=H(hs+z[a[i][j]]);
		h[i]=hs;
	}
	sort(h+1,h+n+1);
	for(l=1;l<=n;l=r+1)
	{
		r=l;while(r<n&&h[r+1]==h[l]) ++r;
		ans=max(ans,r-l+1);
	}
	
	fd(i,1,m)
	{
		int hs=st;
		fd(j,1,n) hs=H(hs+z[a[j][i]]);
		h[i]=hs;
	}
	sort(h+1,h+m+1);
	for(l=1;l<=m;l=r+1)
	{
		r=l;while(r<m&&h[r+1]==h[l]) ++r;
		ans=max(ans,r-l+1);
	}
	
	ans=n+m-ans;
}

T3

二次省流:\(2n\) 个输入但是 fd(i,1,n),唐

赛时怎么调都不过,但是发现一个规律,就是被两个数中间那一段区间完全包含的数不能选,然后想了一个结论但是就过了第一个样例。

正解是按左端点排序+树状数组。

核心代码:

    sort(a+1,a+n+1,my);

    fd(i,1,n)
    {
        (f[i]+=ask(a[i].l)+ask(a[i].r)+2)%=mod;
        add(a[i].r,f[i]);
        (ans+=f[i])%=mod;
    }

然后树状数组要开 \(4\) 倍空间……

T4

第一眼只会暴力,但是写 T3 了就没写……

然后好像可以根号分治?

然后正解不是很会……

总结

  • 还是审题,输入 \(2n\) 个数但是就读入了 \(n\)

  • 准备练一下根号分治

  • DP 大法好

\(\LARGE{唐}\)

posted @ 2024-10-04 14:46  whrwlx  阅读(25)  评论(0编辑  收藏  举报