NOIP模拟90(多校23)

T1 回文

解题思路

原来 \(n^3\) 可以过 500 。。。

先枚举一下路径长度,对于同一路径长度点数最多是 \(n\) 个,我们可以接着枚举从 \((n,m)\) 出发的路径长度相同的点。

然后对于字母相同的位置向下一层转移。

DP 数组可以只开三维 \(f_{i,j,k}\) 表示从 \((1,1)\) 出发到达 \((i,j)\) 点,从 \((n,m)\) 出发的点目前横坐标与出发点差值是 \(k\)

由于路径相同因此我们也可以确定对应的纵坐标,代码实现不难,但是有一点卡常。

code

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=510,M=90,mod=993244853;
int n,m,len,ans,f[N][N][N];
int d1[10]={0,-1,0,-1,0};
int d2[10]={0,0,-1,0,-1};
int d3[10]={0,1,1,0,0};
int d4[10]={0,0,0,1,1};
vector< pair<int,int> > p[N<<1][30],q[N<<1][30];
char s[N][N];
#define add(x,y) {x+=y;if(x>=mod)x-=mod;}
int main()
{
	freopen("palin.in","r",stdin); freopen("palin.out","w",stdout);
	n=read(); m=read(); len=(n+m-1)>>1;
	for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(i+j-1<=len) p[i+j-1][s[i][j]-'a'].push_back(make_pair(i,j));
			else q[(n-i+1)+(m-j+1)-1][s[i][j]-'a'].push_back(make_pair(i,j));
	if(s[1][1]!=s[n][m]) printf("0"),exit(0);
	f[1][1][n]=1;
	for(int l=1;l<=len;l++)
		for(int col=0;col<26;col++)
			for(auto it1:p[l][col])
				for(auto it2:q[l][col])
					for(int k=1;k<=4;k++)
					{
						int i=it1.first,j=it1.second,x=it2.first,y=it2.second;
						int ni=i+d3[k],nj=j+d4[k],nx=x+d1[k],ny=y+d2[k];
						if(s[ni][nj]==s[nx][ny]) add(f[ni][nj][nx],f[i][j][x]);
					}
	if((n+m-1)&1)
	{
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				if((i+j-1)==len+1)
					for(int k=1;k<=4;k++)
						add(ans,f[i+d1[k]][j+d2[k]][i+d3[k]]);
	}
	else
	{
		for(int col=0;col<26;col++)
			for(auto it:p[len][col])
			{
				int i=it.first,j=it.second;
				for(int k=2;k<=3;k++)
				{
					int x=i+d3[k],y=j+d4[k];
					add(ans,f[i][j][x]);
				}
			}
	}
	printf("%d",ans);
	return 0;
}

T2 快速排序

解题思路

发现其实题目给的是对于操作区间的第一个数字为基准进行的快速排序。

那么如果第一个数字是 \(nan\) 的话,显然这次操作不会改变序列中任何一个数字的位置。

那么如果第一个数字不是 \(nan\) 的话,就是把它后面所有小于它的数字都放到它前面,其余的东西照常做就好了。

可以用 multiset 维护或者排序一遍直接指针扫。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=5e5+10;
int T,n,cnt,ans[N];
struct Node{bool jud;int val;}s[N];
multiset< pair<int,int> > res;
bool vis[N];
bool operator < (const Node& x, const Node& y)
{
	if(x.jud || y.jud)
		return false;
	return x.val < y.val;
}
void solve()
{
	n=read(); cnt=0; string ch;
	for(int i=1;i<=n;i++)
	{
		cin>>ch; vis[i]=false; if(ch[0]=='n'){s[i].jud=true;continue;}
		int x=0; for(int j=0;j<ch.size();j++) x=(x<<1)+(x<<3)+(ch[j]^48);
		s[i].jud=false; s[i].val=x; res.insert(make_pair(s[i].val,i));
	}
	for(int i=1;i<=n;i++)
	{
		if(vis[i]) continue;
		if(s[i].jud){ans[++cnt]=-1;continue;}
		while((*res.begin()).first<s[i].val)
		{
			ans[++cnt]=(*res.begin()).first;
			vis[(*res.begin()).second]=true; res.erase(res.begin());
		}
		ans[++cnt]=(*res.begin()).first;
		vis[(*res.begin()).second]=true; res.erase(res.begin());
	}
	for(int i=1;i<=n;i++) if(ans[i]==-1) printf("nan ");else printf("%lld ",ans[i]);
	putchar('\n');
}
#undef int
int main()
{
	#define int long long
	freopen("qsort.in","r",stdin); freopen("qsort.out","w",stdout);
	T=read(); while(T--) solve();
	return 0;
}

T3 混乱邪恶

解题思路

好像是一道高联题,然而我并不是特别会证明。

肯定是要先排序的,那么先让 \(2i\)\(2i-1\) 分到不同的组,两个的差值记为 \(d_i\)

然后对于所有的 \(d\) 进行排序选择最大的与最小的相减,将差值计入集合,相当于是把较小值所代表的集合的符号进行了更改。

最后 DFS 一遍输出答案就好了。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=2e6+10;
int n,m,cnt,id,all,d[N],ans[N];
struct Node{int id,dat;}s[N];
vector< pair<int,int> > v[N];
multiset< pair<int,int> > res;
bool comp(Node x,Node y){if(x.dat!=y.dat)return x.dat<y.dat;return x.id<y.id;}
void dfs(int x,int flag)
{
	if(!v[x].size()) return ans[s[x].id]=flag,void();
	for(auto it:v[x])
	dfs(it.first,flag*it.second);
}
#undef int
int main()
{
	#define int long long
	freopen("chaoticevil.in","r",stdin); freopen("chaoticevil.out","w",stdout);
	cnt=n=read(); m=read(); printf("NP-Hard solved\n");
	for(int i=1;i<=n;i++) s[i].dat=read(),s[i].id=i; if(n&1) n++;
	sort(s+1,s+n+1,comp); id=n;
	for(int i=1;i<=n/2;i++)
		all+=(s[2*i].dat-s[2*i-1].dat!=1),id++,v[id].push_back(make_pair(2*i-1,-1)),
		v[id].push_back(make_pair(2*i,1)),res.insert(make_pair(s[2*i].dat-s[2*i-1].dat,id));
	while(all)
	{
		auto t1=*res.begin(),t2=*res.rbegin();
		all-=(t1.first!=1)+(t2.first!=1); all+=(t2.first-t1.first!=1);
		pair<int,int> temp=make_pair(t2.first-t1.first,++id);
		v[id].push_back(make_pair(t1.second,-1)); v[id].push_back(make_pair(t2.second,1));
		res.erase(res.find(t1)); res.erase(res.find(t2)); res.insert(temp);
	}
	int flag=1; for(auto it:res) dfs(it.second,flag),flag=-flag;
	for(int i=1;i<=cnt;i++) printf("%lld ",ans[i]);
	return 0;
}

T4 校门外歪脖树上的鸽子

解题思路

\([l,r]\) 区间上的修改视为 \(l-1\) 以及 \(r+1\) 这两个点所连接的整个路径。

开两颗线段树维护,对于左边的这条链每一次给对应父亲节点的右子树加入贡献,右边的这条链则相反。

对于询问操作也是类似的进行查询。

code

#include<bits/stdc++.h>
#define int long long
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=4e5+10;
int n,m,root,tim,dfn[N],siz[N],son[N],topp[N],fa[N],dep[N],id[N],ch[N][2],vis[N];
struct Segment_tree
{
	int bas[N];
	struct Node{int l,r,dat,laz,siz;}tre[N<<2];
	inline void add(int x,int val){tre[x].dat+=val*tre[x].siz;tre[x].laz+=val;}
	#define push_up(x) tre[x].dat=tre[ls].dat+tre[rs].dat
	#define push_down(x) add(ls,tre[x].laz),add(rs,tre[x].laz),tre[x].laz=0
	void build(int x,int l,int r)
	{
		if(l==r) return tre[x].siz=bas[l],void();
		int mid=(l+r)>>1; build(ls,l,mid); build(rs,mid+1,r);
		push_up(x); tre[x].siz=tre[ls].siz+tre[rs].siz;
	}
	void insert(int x,int l,int r,int L,int R,int val)
	{
		if(L>R) return ;
		if(L<=l&&r<=R) return add(x,val),void();
		int mid=(l+r)>>1; if(tre[x].laz) push_down(x);
		if(L<=mid) insert(ls,l,mid,L,R,val);
		if(R>mid) insert(rs,mid+1,r,L,R,val);
		push_up(x);
	}
	int query(int x,int l,int r,int L,int R)
	{
		if(L<=l&&r<=R) return tre[x].dat;
		int mid=(l+r)>>1,sum=0; if(tre[x].laz) push_down(x);
		if(L<=mid) sum+=query(ls,l,mid,L,R);
		if(R>mid) sum+=query(rs,mid+1,r,L,R);
		return sum;
	}
}L,R;
void dfs1(int x)
{
	if(!ch[x][0]&&!ch[x][1]) siz[x]=1;
	for(int i=0;i<=1;i++)
	{
		int to=ch[x][i]; if(!to) continue;
		fa[to]=x; dep[to]=dep[x]+1; dfs1(to);
		siz[x]+=siz[to]; if(siz[son[x]]<siz[to]) son[x]=to;
	}
}
void dfs2(int x,int tp)
{
	topp[x]=tp; dfn[x]=++tim;
	if(son[x]) dfs2(son[x],tp);
	for(int i=0;i<=1;i++)
		if(ch[x][i]&&!dfn[ch[x][i]])
			dfs2(ch[x][i],ch[x][i]);
	R.bas[dfn[ch[x][1]]]=siz[ch[x][0]];
	L.bas[dfn[ch[x][0]]]=siz[ch[x][1]];
}
void update(int x,int y,int val)
{
	x=id[x-1]; y=id[y+1]; int lasx=x,lasy=y;
	while(topp[x]^topp[y])
		if(dep[topp[x]]<dep[topp[y]]) 
			R.insert(1,1,tim,dfn[topp[y]],dfn[y],val),lasy=topp[y],y=fa[topp[y]];
		else L.insert(1,1,tim,dfn[topp[x]],dfn[x],val),lasx=topp[x],x=fa[topp[x]];
	if(dep[x]<dep[y]) R.insert(1,1,tim,dfn[x]+2,dfn[y],val),L.insert(1,1,tim,dfn[lasx],dfn[lasx],-val);
	else L.insert(1,1,tim,dfn[y]+2,dfn[x],val),R.insert(1,1,tim,dfn[lasy],dfn[lasy],-val);
}
int query(int x,int y)
{
	x=id[x-1]; y=id[y+1]; int lasx=x,lasy=y,sum=0;
	while(topp[x]^topp[y])
		if(dep[topp[x]]<dep[topp[y]]) 
			sum+=R.query(1,1,tim,dfn[topp[y]],dfn[y]),lasy=topp[y],y=fa[topp[y]];
		else sum+=L.query(1,1,tim,dfn[topp[x]],dfn[x]),lasx=topp[x],x=fa[topp[x]];
	if(dep[x]<dep[y]) sum+=R.query(1,1,tim,dfn[x]+2,dfn[y])-L.query(1,1,tim,dfn[lasx],dfn[lasx]);
	else sum+=L.query(1,1,tim,dfn[y]+2,dfn[x])-R.query(1,1,tim,dfn[lasy],dfn[lasy]);
	return sum;
}
#undef int
int main()
{
	#define int long long
	freopen("pigeons.in","r",stdin); freopen("pigeons.out","w",stdout);
	int opt,x,y,z; n=read(); m=read();
	for(int i=1;i<n;i++) vis[ch[i+n][0]=read()]=true,vis[ch[i+n][1]=read()]=true;
	for(int i=1;i<=2*n;i++) if(!vis[i]){root=i;break;}
	ch[2*n+1][0]=2*n; ch[2*n+1][1]=root; ch[2*n+3][0]=2*n+1; ch[2*n+3][1]=2*n+2;
	for(int i=1;i<=n;i++) id[i]=i; id[0]=2*n; id[n+1]=2*n+2; root=2*n+3;
	dfs1(root); dfs2(root,root); L.build(1,1,tim); R.build(1,1,tim);
	while(m--){opt=read(); x=read(); y=read();if(opt==1) z=read(),update(x,y,z);else printf("%lld\n",query(x,y));}
	return 0;
}
posted @ 2021-11-05 21:32  Varuxn  阅读(157)  评论(0编辑  收藏  举报