Loading

Noip模拟90 2021.11.5(最长题解)

T1 回文

这种经典的\(dp\)就是不会做,就是不会。。。。。

除了\(dp\)数组不会设其他的都跟题解差不多,然而并没有啥用。。。。

\(dp[len][x1][x2]\)表示回文串的一半长度为\(len\),从\((1,1)\)开始的串这时终点的横坐标为\(x1\),从\((n,m)\)开始的串终点的横坐标为\(x2\)

然后分情况转移即可

最后在一条对角线,也就是回文中心处统计答案

palin
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=501,mod=993244853;
namespace AE86{
	FILE *wsn;
	auto read=[](){
		int x=0,f=1;char ch=getchar();
		while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
		return x*f;
	};
	auto write=[](int x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
	};
}using namespace AE86;
int n,m,a[NN][NN],len;
int dp[2][NN][NN];
char s[NN];
namespace WSN{
	inline short main(){
		wsn=freopen("palin.in","r",stdin);
		wsn=freopen("palin.out","w",stdout);
		n=read(); m=read(); len=n+m-2>>1;
		for(int i=1;i<=n;i++){scanf("%s",s+1);
			for(int j=1;j<=m;j++)a[i][j]=s[j]-'a';
		}
		if(a[1][1]!=a[n][m]) return puts("0"),0;
		dp[0][1][n]=1;
		for(int i=0;i<len;i++){
			int x1=min(n,i+1),x2=max(1ll,n-i);
			memset(dp[i+1&1],0,sizeof(dp[i+1&1]));
			for(int j=1;j<=x1;j++){
				int y1=i-j+2;
				for(int k=n;k>=x2;k--){
					int y2=n+m-k-i;
					if(j<n&&y2>1) if(a[j+1][y1]==a[k][y2-1]) (dp[i+1&1][j+1][k]+=dp[i&1][j][k])%=mod;
					if(j<n&&k>1)  if(a[j+1][y1]==a[k-1][y2]) (dp[i+1&1][j+1][k-1]+=dp[i&1][j][k])%=mod;
					if(y1<m&&y2>1)if(a[j][y1+1]==a[k][y2-1]) (dp[i+1&1][j][k]+=dp[i&1][j][k])%=mod;
					if(y1<m&&k>1) if(a[j][y1+1]==a[k-1][y2]) (dp[i+1&1][j][k-1]+=dp[i&1][j][k])%=mod;
				}
			}
		}
		int ans=0;
		for(int i=1;i<=n;i++){
			(ans+=dp[len&1][i][i])%=mod;
			if((n+m&1)&&i<n) (ans+=dp[len&1][i][i+1])%=mod;
		} write(ans);
		return 0;
	}
}
signed main(){return WSN::main();}

T2 快速排序

确实像大结论题,不过觉得考场上差点就分析出来了,可是最终还是没有。。。。

发现他排序的过程在以\(nan\)\(left\)的时候,\(nan\)的相对位置不会变

所以直接扫一遍,扫到一个不是\(nan\)的数就把小于这个数的数全部输出

\(nan\)直接输出\(nan\)

qsort
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int NN=5e5+5;
namespace AE86{
	FILE *wsn;
	auto read=[](){
		int x=0;bool f=1;char ch=getchar();
		while(ch<'0'||ch>'9'){if(ch=='a'){f=0,ch=getchar();break;}ch=getchar();}
		if(!f) return -1ll;
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
		return x;
	};
	auto write=[](int x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
	};
}using namespace AE86;
int T,n,stk[NN],top;
struct number{bool isnan;int value;}a[NN];
multiset<int> s;
auto solve=[](){
	n=read(); s.clear();
	for(int i=1;i<=n;i++){
		int x=read();if(x>0)s.insert(x);
		a[i]=x<0?number{1,x}:number{0,x};
	}
	for(int i=1;i<=n;i++){
		if(a[i].isnan) printf("nan ");
		else{
			if(s.find(a[i].value)==s.end())continue;
			multiset<int>::iterator it=s.begin();
			while(*it<a[i].value){
				stk[++top]=*it;
				++it;
			}
			for(int j=1;j<=top;j++)
				s.erase(s.find(stk[j])),printf("%lld ",stk[j]);
			top=0; printf("%lld ",*it); s.erase(it);
		}
	}
	puts("");
};
namespace WSN{
	inline short main(){
		wsn=freopen("qsort.in","r",stdin);
		wsn=freopen("qsort.out","w",stdout);
		T=read();while(T--)solve();
		return 0;
	}
}
signed main(){return WSN::main();}

T3 混乱邪恶

\(OMA\)的委托,决定把这道题的题解写成论文。。。。。

\(\color{red}{\huge{Warning}}\)

考虑这道题的限制会把原来的数组变成什么样子

\(\{a_1,a_2,\dots,a_n\}\)\(\{1,2,\dots,m\}\)的子集

这个保证了所有的\(a_i\)互不相同,并且值域确定了

\(\lfloor\frac{2m}{3}\rfloor <n\leq m\)

这个保证了\(a\)数组中一定有一串等差数列,并且公差为\(1\)

这个不难证明,因为在\([1,m]\)中最多只有\(\frac{m}{2}\)个偶数或者奇数,

如果保证两个数之间的差值都大于\(1\)显然是不可能的,因为\(n\in (\frac{2m}{3},m]\)

所以排完序之后一定有一段序列是公差为\(1\)的等差数列,即奇数偶数都有

保证\(\sum a_i\)为偶数

这个限制保证了\(a\)数组中一定有偶数个奇数

考虑如果没有限制的情况下,数组\(a\)有几种情况

不难发现,数组\(a\)要么都是偶数,要么有偶数个奇数,否则无法满足\(\sum a_i\in even\)

而前面的限制保证了\(a\)数组无法都是偶数,那么数组中只能有偶数个奇数

这样我们就分析出来了数组\(a\)构造上的几个性质:

  1. 所有数互不相同,值域在\([1,m]\)之间
  2. 一定有一串公差为一的等差数列
  3. 数组中一定有偶数个奇数

然后我们考虑如何构造。

如果数列长度为奇数,我们给他再加上一个元素\(0\),这样就保证了数组长度为偶数,以便我们后面分组

我们把数组\(a\)进行升序排序,然后相邻的两两为一组,并记录\(d_i=a_{2i}-a_{2i-1}\)

然后我们记录\(sm=\sum d_i\),这个值是我们“预构造”出的方案的偏差值(即和正确的构造方案多了多少数)

然后我们按照\(d_i\)降序的顺序进行“选择”

发现差值为\(d_i\)的一组元素如果交换位置关系,即\(a_{2i},a_{2i-1}\)交换

会对\(sm\)产生\(2d_i\)的影响,即\(sm-2d_i\),那么我们“选择”的过程就是消除原本的构造方案差值的过程

这样,我们就实现了对这道题的构造,并没有不合法的方案(如果你不想看证明了,现在就可以开始愉快的切题了)

我们开始大面积证明这样构造的正确性。。。。

首先,显然,加入一个元素\(0\)不会对答案产生影响

那么我们考虑为什么一定没有不合法方案

发现每次交换元素位置产生的总贡献一定是一个偶数,那么我们现在要证明的问题变为了\(\sum d_i\in even\)

start

我们构造的前提是数组长度为偶数,又有偶数个奇数,则一定有偶数个偶数

那么分两种情况讨论,

  1. 偶数个数大于奇数

这时我们排完序后的序列一部分肯定是奇数偶数交错的,这时两两分组一定会产生偶数对儿奇数偶数匹配

他们的差值是奇数,有偶数个差值,这一部分的\(\sum d\)为偶数,剩下的更好说,偶数偶数配对差值都是偶数

这种情况得证

  1. 偶数个数小于奇数

前面奇数偶数交错的部分跟前面一样,后面剩下的一定是偶数个奇数,他们匹配差值都是偶数,也得证

end

证明完\(\sum d\)为偶数后,发现证明的问题为差值从大到小进行消除一定有合法的解

不难发现如果所有的差值都被选择最终\(sm<0\)(因为\(sm\)\(d\)加出来的,拿\(2d\)去减它一定会使他小于零)

而我们会想到有一种情况可能会无解,即当\(sm=2\)最小的差值\(2\)或者更大,这样减完就会是负的,\(2-2*2<0\)

那么我们现在要找一种让差值尽可能都\(\geq 2\)的数列,如果他也有解,那么所有的的数列都有解,所有的问题就都解决了

那么我们考虑把\([1.m]\)中的偶数都选上,这样会有\(\frac{m}{2}\)个偶数,那么剩下的\(\frac{2m}{3}-\frac{m}{2}\)个数只能是奇数

不妨把他们规定为\(1,3,5,...\),因为其他的跟这几个都一样的

我们发现有长度为\(2\times(\frac{2m}{3}-\frac{m}{2})\)的公差为一的等差数列

这样两两分组后差值为\(1\)的二元组就会有\(\frac{2m}{3}-\frac{m}{2}=\frac{m}{6}\)这么多个,剩下\(\frac{n}{2}-\frac{m}{6}\)组偶数保证其差值\(\geq 2\)

然后我们发现\(sm\leq m-\frac{n}{2}<n\)(唯独不知道这里是为什么,题解上说的就用吧)

\(\color{red}{馍馍zxs证明已经给到下面了}\)

确实不用考虑那个加一的问题,因为下面的不等式部分取值极限取不到(比如n>\(\frac{2m}{3}\),这样取到这个值最后的不等式符号是小于,加上\(1\)之后顶多是小于等于\(0\)

那么\(sm-4\times(\frac{n}{2}-\frac{m}{6})\leq m-\frac{n}{2}-2n+\frac{2m}{3}\leq \frac{5m}{3}-\frac{5n}{2}\leq \frac{5m}{3}-\frac{5}{2}\times \frac{2m}{3}< 0\)

所以不用差值为一的二元组就可以把\(sm\)消完,如果发现\(sm-2*d<0\)则可以找前面的差值为\(1\)的去消,那么以上的构造都是合理的

\(\color{red}{\huge{证完啦,手残啦!!!}}\)

不过我会很佩服能够从上面一直看到这里的人,我反正是没有这个耐心的(向你致敬~~)

chaoticevil
#include<bits/stdc++.h>
using namespace std;
const int NN=1e6+5;
namespace AE86{
	FILE *wsn;
	auto read=[](){
		int x=0,f=1;char ch=getchar();
		while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
		return x*f;
	};
	auto write=[](int x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
	};
}using namespace AE86;
int n,m,sm,c[NN];
vector<pair<int,int> > t;
struct node{int val,id;}a[NN];
namespace WSN{
	inline short main(){
		wsn=freopen("chaoticevil.in","r",stdin);
		wsn=freopen("chaoticevil.out","w",stdout);
		n=read();m=read();
		for(int i=1;i<=n;++i)a[i]=node{read(),i};
		if(n&1) a[++n]=node{0,n};
		sort(a+1,a+n+1,[](node x,node y)->bool{return x.val<y.val;});
		for(int i=1,d;i<=n;i+=2){
			d=a[i+1].val-a[i].val;
			t.push_back(make_pair(d,i)); sm+=d;
			c[a[i+1].id]=1; c[a[i].id]=-1;
		}
		sort(t.begin(),t.end());
		for(int i=t.size()-1,v,id;~i;--i){
			v=t[i].first,id=t[i].second;
			if(sm>=v*2)
				sm-=v*2,swap(c[a[id].id],c[a[id+1].id]);
			else if(sm==0){
				puts("NP-Hard solved");if(!a[1].val)--n;
				for(int j=1;j<=n;++j)printf("%d ",c[j]);
				puts(""); return 0;
			}
		}
		return 0;
	}
}
signed main(){return WSN::main();}

T4 校门外歪脖树上的鸽子

正在改,拼命改

\(upd 2021.11.7\)

pigeons
#include<map>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int NN=4e5+5;																																																											namespace AE86{FILE *wsn;auto read=[](){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;};auto write=[](int x,char opt='\n'){char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);};}using namespace AE86;
int n,m,rng,ch[NN][2],fa[NN],root;
int ll[NN],rr[NN],len[NN],lst[NN],rst[NN];
map<int,int>mp[NN];
inline int pos(int x){return ch[fa[x]][1]==x;}
inline int bro(int x){return x==root?root:ch[fa[x]][pos(x)^1];}
inline void build(int x){
	ll[x]=rr[x]=x;
	if(ch[x][0])build(ch[x][0]),ll[x]=ll[ch[x][0]];
	if(ch[x][1])build(ch[x][1]),rr[x]=rr[ch[x][1]];
	if(ll[x]>rr[x]) swap(ch[x][0],ch[x][1]),ll[x]=ll[ch[x][0]],rr[x]=rr[ch[x][1]];
	len[x]=rr[x]-ll[x]+1; mp[ll[x]][rr[x]]=x;
}
namespace tree_division{
	int dfn[NN],rk[NN],dep[NN],siz[NN],son[NN],top[NN],cnt;
	inline void dfs1(int f,int x){
		dep[x]=dep[f]+1; siz[x]=1;
		if(x==root) lst[x]=rst[x]=x;
		else lst[x]=pos(x)?x:lst[fa[x]],rst[x]=pos(x)?rst[fa[x]]:x;
		if(ch[x][0]) dfs1(x,ch[x][0]),siz[x]+=siz[ch[x][0]];
		if(ch[x][1]) dfs1(x,ch[x][1]),siz[x]+=siz[ch[x][1]];
		son[x]=(siz[ch[x][0]]>siz[ch[x][1]])?ch[x][0]:ch[x][1];
	}
	inline void dfs2(int x,int t){
		top[x]=t; dfn[x]=++cnt; rk[cnt]=x;if(!son[x]) return; dfs2(son[x],t);
		(son[x]==ch[x][0])?dfs2(ch[x][1],ch[x][1]):dfs2(ch[x][0],ch[x][0]);
	}
	inline int LCA(int x,int y){
		while(top[x]!=top[y]){
			if(dep[top[x]]<dep[top[y]])swap(x,y);x=fa[top[x]];
		}if(dfn[x]>dfn[y]) swap(x,y);
		return x;
	}
	inline int findrt(int x,int y){
		x=top[x];
		while(x!=top[y])if(fa[x]==y)return x;else x=top[fa[x]];
		return son[y];
	}
}using namespace tree_division;
struct SNOWtree{
	#define lid (id<<1)
	#define rid (id<<1|1)
	int ll[NN<<2],rr[NN<<2],w[NN<<2],sm[NN<<2],laz[NN<<2];
	inline void pushup(int id){if(ll[id]!=rr[id])sm[id]=sm[lid]+sm[rid];}
	inline void down(int id,int v){sm[id]+=v*w[id];laz[id]+=v;}
	inline void pushdown(int id){if(ll[id]!=rr[id]&&laz[id])down(lid,laz[id]),down(rid,laz[id]),laz[id]=0;}
	inline void build(int id,int l,int r,int opt){
		ll[id]=l;rr[id]=r;if(l==r)return w[id]=(pos(bro(rk[l]))!=opt)?len[bro(rk[l])]:0,void();
		int mid=(l+r)>>1; build(lid,l,mid,opt); build(rid,mid+1,r,opt); w[id]=w[lid]+w[rid];
	}
	inline void modify(int id,int l,int r,int v){
		if(l<=ll[id]&&rr[id]<=r) return down(id,v),void(); pushdown(id);
		int mid=ll[id]+rr[id]>>1;if(l<=mid)modify(lid,l,r,v);if(r>mid)modify(rid,l,r,v);pushup(id);
	}
	inline int query(int id,int l,int r){
		if(l<=ll[id]&&rr[id]<=r)return sm[id];pushdown(id);int mid=ll[id]+rr[id]>>1,ans=0;
		if(l<=mid) ans+=query(lid,l,r);if(r>mid) ans+=query(rid,l,r); return ans;
	}
	inline void update(int x,int y,int v){
		while(top[x]!=top[y]){
			if(dep[top[x]]<dep[top[y]])swap(x,y);
			modify(1,dfn[top[x]],dfn[x],v);x=fa[top[x]];
		}if(dfn[x]>dfn[y])swap(x,y);modify(1,dfn[x]+1,dfn[y],v);
	}
	inline int calc(int x,int y,int ans=0){
		while(top[x]!=top[y]){
			if(dep[top[x]]<dep[top[y]])swap(x,y);
			ans+=query(1,dfn[top[x]],dfn[x]);x=fa[top[x]];
		}if(dfn[x]>dfn[y])swap(x,y);return ans+query(1,dfn[x]+1,dfn[y]);
	}
}tr[2];
inline void update(int x,int v){tr[pos(x)^1].modify(1,dfn[bro(x)],dfn[bro(x)],v);}
inline int query(int x){return tr[pos(x)^1].query(1,dfn[bro(x)],dfn[bro(x)]);}
int opt,x,y,d,lca;
auto solve=[](){
	opt=read();x=read();y=read();lca=LCA(x,y);
	// cout<<opt<<" "<<x<<" "<<y<<" "<<lca<<endl;
	if(opt==1){
		d=read();
		if(mp[x].find(y)!=mp[x].end()) return update(mp[x][y],d),void();
		int X=lst[x],Y=rst[y];
		if(dep[X]<=dep[lca]) update(findrt(x,lca),d);
		else update(X,d),tr[0].update(X,findrt(x,lca),d);
		if(dep[Y]<=dep[lca]) update(findrt(y,lca),d);
		else update(Y,d),tr[1].update(Y,findrt(y,lca),d);
	}else{
		if(mp[x].find(y)!=mp[x].end()) return write(query(mp[x][y])),void();
		int X=lst[x],Y=rst[y],ans=0;
		if(dep[X]<=dep[lca]) ans+=query(findrt(x,lca));
		else ans+=query(X)+tr[0].calc(X,findrt(x,lca));
		if(dep[Y]<=dep[lca]) ans+=query(findrt(y,lca));
		else ans+=query(Y)+tr[1].calc(Y,findrt(y,lca));
		write(ans); return;
	}
};
namespace WSN{
	inline short main(){
		wsn=freopen("pigeons.in","r",stdin);
		wsn=freopen("pigeons.out","w",stdout);
		n=read();m=read();rng=2*n-1;
		for(int i=n+1;i<=rng;i++){
			ch[i][0]=read();ch[i][1]=read();
			fa[ch[i][0]]=fa[ch[i][1]]=i;
		} for(int i=n+1;i<=rng;i++)if(!fa[i]){root=i;break;}
		build(root);dfs1(0,root);dfs2(root,root);
		tr[0].build(1,1,rng,0);tr[1].build(1,1,rng,1);
		while(m--)solve();
		return 0;
	}
}
signed main(){return WSN::main();}
posted @ 2021-11-05 19:35  雪域亡魂  阅读(274)  评论(7编辑  收藏  举报