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();}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM