11.22 NOIP2024模拟赛#26

又挂分了

T1

发现 \(n\) 很小,有效操作数很少

然后可以随机化+卡时

喜提最劣解

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define ll long long
#define fd(i,a,b) for(int i=(a);i<=(b);i=-~i)
#define bd(i,a,b) for(int i=(a);i>=(b);i=~-i)
#define db(x) cout<<"DEBUG "<<#x<<" = "<<x<<endl;
#define endl '\n'
using namespace std;

//#define SIZE (1<<20)
//char In[SIZE],Out[SIZE],*p1=In,*p2=In,*p3=Out;
//#define getchar() (p1==p2&&(p2=(p1=In)+fread(In,1,SIZE,stdin),p1==p2)?EOF:*p1++)
template<typename _T=int>
inline _T read()
{
	_T x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c-48);c=getchar();}
	return x*f;
}

const int N=5+9,M=1e6+509,mod=998244353,MX=(1ll<<15)-1;
const double tim=0.9;
mt19937_64 rnd(time(0));

int n,ans;
int a[N][N],b[N][N],ol[N][N];

inline void clear()
{
	fd(i,1,n) fd(j,1,i) a[i][j]=ol[i][j];
}

inline int calc()
{
	int res=0;
	fd(i,1,n) fd(j,1,i) res+=(a[i][j]^b[i][j]);
	return res;
}

inline void turn()
{
	static int nw[N][N];
	fd(j,1,n) fd(i,j,n) nw[i][j]=a[n-j+1][i-j+1];
	fd(i,1,n) fd(j,1,i) a[i][j]=nw[i][j];
//	fd(i,1,n)
//	{
//		fd(j,1,i) cerr<<a[i][j]<<' ';
//		cerr<<endl;
//	}
}

void chg()
{
	static int nw[N][N];
	fd(i,1,n) fd(j,1,i) nw[i][j]=a[i][i-j+1];
	fd(i,1,n) fd(j,1,i) a[i][j]=nw[i][j];
//	fd(i,1,n)
//	{
//		fd(j,1,i) cerr<<a[i][j]<<' ';
//		cerr<<endl;
//	}
}

signed main()
{
#define FJ
#ifdef FJ
	freopen("triangle.in","r",stdin);
	freopen("triangle.out","w",stdout);
#else
	freopen("ex_triangle2.in","r",stdin);
//	freopen("triangle.out","w",stdout);
#endif
//#define io
#ifdef io
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
#endif
	
	n=read();
	fd(i,1,n) fd(j,1,i) a[i][j]=ol[i][j]=read();
	fd(i,1,n) fd(j,1,i) b[i][j]=read();
	
	ans=calc();
	
	while((double)clock()/CLOCKS_PER_SEC<tim)//mt19937_64保佑
	{
		int op=rnd()&1;
		if(op) turn();
		else chg();
		ans=min(ans,calc());
	}
	
	printf("%lld",ans);
	
	return 0;
}

T2

发现选的一定是第一个 \(1\)

然后把第一个 \(1\) 与之后的移到第一个 \(1\) 后的第一个 \(0\) 的位置

写完发现过不去小样例

然后需要特判第一个 \(1\) 之后是否全为 \(1\)

如果是,那么就需要考虑是否有 \(0\),有的话直接输出,没有的话把最后一位变成 \(0\)

然后还要判断是否全是 \(0\),如果是,直接输出 \(0\)

然后第二个大样例过不去

发现还要考虑以第一个 \(1\) 开头的连续的 \(1\) 的长度和以第一个 \(1\) 后的第一个 \(0\) 为开头的连续 \(0\) 的长度的大小关系

因为你可能会把后面的某个 \(1\) 变成 \(0\),然后不优了

然后加个偏移量 A 掉

但是我 \(10^7\) 的大样例跑了 \(6s\),改成 putchar 变成 \(0.6s\),神奇

点击查看代码
#include<bits/stdc++.h>
//#define int long long
#define ll long long
#define fd(i,a,b) for(int i=(a);i<=(b);i=-~i)
#define bd(i,a,b) for(int i=(a);i>=(b);i=~-i)
#define db(x) cout<<"DEBUG "<<#x<<" = "<<x<<endl;
#define endl '\n'
using namespace std;

//#define SIZE (1<<20)
//char In[SIZE],Out[SIZE],*p1=In,*p2=In,*p3=Out;
//#define getchar() (p1==p2&&(p2=(p1=In)+fread(In,1,SIZE,stdin),p1==p2)?EOF:*p1++)
template<typename _T=int>
inline _T read()
{
	_T x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c-48);c=getchar();}
	return x*f;
}

const int N=1e7+509,M=1e6+509,mod=998244353;

int n,p1,p2,len1,len0,len,fl1,fl0;
char num[N],num2[N];

inline void init()
{
	n=read();
	fl1=fl0=0,p1=-1,p2=-1,len=len1=len0=0;
	scanf("%s",num+1);
	fd(i,1,n) if(num[i]=='1'){p1=i;break;}
	fd(i,1,n) if(num[i]=='0'){fl0=1;break;}
	fd(i,p1,n) if(num[i]=='0'){p2=i;break;}
	len=n-p2+1;
//	cerr<<p1<<' '<<p2<<' '<<len<<endl;
}

void solve()
{
	fd(i,1,n) num2[i]=num[i];
	if(p1==-1)
	{
		putchar('0');
		putchar('\n');
		return;
	}
	if(p2==-1)
	{
		if(fl0) fd(i,p1,n) putchar(num2[i]);
		else
		{
			fd(i,p1,n-1) putchar(num2[i]);
			putchar('0');
		}
		putchar('\n');
		return;
	}
	fd(i,p1,n)
	{
		if(num[i]=='0') break;
		++len1;
	}
	fd(i,p2,n)
	{
		if(num[i]=='1') break;
		++len0;
	}
//	cerr<<"len1="<<len1<<' '<<"len0="<<len0<<endl;
	const int ad=(len1>len0)?len1-len0:0;
	fd(i,1,len)
	{
		const int pos1=p1+i+ad-1,pos2=p2+i-1;
		num2[pos2]=(char)(((num[pos1]-'0')^(num[pos2]-'0'))+'0');
	}
	fd(i,p1,n) if(num2[i]=='1'){fl1=1;break;};
	if(fl1) fd(i,p1,n) putchar(num2[i]);
	else putchar('0');
	putchar('\n');
}

inline void Main()
{
	init();
	solve();
}

signed main()
{
#define FJ
#ifdef FJ
	freopen("xor.in","r",stdin);
	freopen("xor.out","w",stdout);
#else
//	freopen("ex_xor1.in","r",stdin);
//	freopen("xor.out","w",stdout);
#endif
//#define io
#ifdef io
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
#endif
	
	int T=read();
	while(T--) Main();
	
	return 0;
}

T3

真的不会 QwQ

码的暴力

然而本来应该存到 lim 数组里的却存到 s (dfs 用到的数组)里了

然鹅过了小样例……

但是在 \(\text{Subtask}\) 的情况下居然有 \(5\)

本来 \(20\) 的()

正解是矩乘优化 \(dp\)

这是我的一部分代码(雾):

if(n<=10)
{
	ans=0;
	fd(i,1,m) s[k[i]]=a[i];
	dfs(1);
	printf("%lld\n",ans);
}
else if(c==2)
{
	//真的不会 QwQ
}
else if(m==0)
{
	//真的不会 QwQ
}

T4

码的线段树,但是码红温了,最后 \(10min\) 测一下大样例,跑了 \(5min\),但是没错,于是交了

\(25\) 分,没挂分

赛后发现,对于满二叉树的情况,可以在 \(O(\log)\) 的时间里解决询问

对于链的情况,直接 dfs 序即可

然后正解很妙,是拆贡献

点击查看正解

\(x\) 在遍历序列中的位置,等价于求有多少个点在 \(x\) 前被遍历。

考虑一下哪些情况下 \(y\) 会比 \(x\) 早遍历。

分别用 \(lson(x)\)\(rson(x)\) 表示 \(x\) 的左右子树,用 \(sub(x)\) 表示 \(x\) 的子树,\(op_x\in\{-1,0,1\}\) 表示 \(x\) 的遍历方式,不难发现只有以下几种情况:

  • \(y\in lson(x),op_x=-1\)

  • 存在 \(z\),使得 \(y\in lson(z),x\in rson(z)\)

  • \(x\in sub(y),op_y=-1\)

  • \(x\in rson(y),op_y=0\)

考虑把这四类分开计算贡献。

首先,前两类可以直接预处理。

对于后两类,考虑按编号分块,处理每个 \(y\)\(x\) 的贡献。

由于只有区间赋值操作,所以可以把所有块分为两类:第一类为整个块所有点的遍历方式相同的块,第二类为其它块。

分别考虑两类块对 \(x\) 的贡献。

可以发现,第一类块对 \(x\) 的贡献只需要知道这个块内有多少 \(y\),满足 \(y\)\(x\) 的祖先,和有多少 \(y\),满足 \(x\)\(y\) 的右子树内。

考虑枚举 \(y\),然后就相当于若干次子树加,直接差分前缀和即可 \(O(n\sqrt n)\)

\(f_x\) 为所有第二类块内的点对 \(x\) 的贡献之和。

如果只有单点修改,可以发现修改一个点会对这个点的子树或右子树的 \(f_x\) 产生影响。

由于只存在区间赋值操作,所以每次操作最多新增
\(2\) 个二类块,在修改的时候如果遇到二类块就直接遍历这个块,然后把这个块变成一类块。

这样总共会有 \(O(n\sqrt n)\) 次单点修改,\(O(n)\) 次查询,写个 \(O(1)-O(\sqrt n)\) 的分块平衡一下,总复杂度 \(O(n\sqrt n)\)

总结

  • T3 挂分,dp 还需要练,感觉想出来柿子就差不多可以 A 了
  • T4 的话,虽然分块还算熟练,但是拆贡献和根号科技啥的还需要加强
posted @ 2024-11-22 19:39  whrwlx  阅读(2)  评论(0编辑  收藏  举报