NOI2022 選做

NOI2022 選做

四年前的今天,我在機房暢想着未知的未來。
現在,時間過去了,身邊的人變了,我還在電腦前,追逐我的夢想。

%%%ljc1301 爺

「NOI2022」衆數

首先有個經典算法:若序列中存在一個數出現次數 \(>\lfloor \frac n2\rfloor\),那麼執行下列算法即可找到這個數:

  • \(i=1,count=1,value=a_1\)
  • \(i\leftarrow i+1\)。若 \(value=a_i\),令 \(count=count+1\)。否則,令 \(count=count-1\)
  • \(count=0\),令 \(count=1,value=a_i\)
  • 重複執行操作 \(2,3\) 直到 \(i=N\)

觀察這個算法的過程,是一個將兩個不同的數消去的過程。

因此可以用一課值域線段樹維護值,再用鏈表和線段樹合併維護合併。時間 \(O(n\log n)\)

點擊查看代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define fi first
#define se second
template <class T>
inline void read(T &x)
{
	x=0; int f=1; char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
	x=(f==1)?x:-x;
}
#define pc putchar
template <class T>
void print(T x)
{
	if(x<0) pc('-'),x=-x;
	if(x>9) print(x/10);
	pc(x%10+'0');
}
const int maxn=1000005;
int n,q,a[maxn],siz[maxn],rt[maxn],ls[maxn*24],rs[maxn*24],sz; pii t[maxn*24];
int he[maxn],ta[maxn],pr[maxn],su[maxn],v[maxn],tot;
inline pii operator + (pii a,pii b)
{
	if(a.fi==0) return b;
	if(b.fi==0) return a;
	if(a.fi==b.fi) return pii(a.fi,a.se+b.se);
	if(a.se==b.se) return pii(0,0);
	if(a.se>b.se) return pii(a.fi,a.se-b.se);
	return pii(b.fi,b.se-a.se);
}
inline void pushup(int rt) { t[rt]=t[ls[rt]]+t[rs[rt]]; }
void update(int &rt,int l,int r,int u,int v)
{
	if(!rt) rt=++sz;
	if(l==r)
	{
		t[rt].se+=v;
		t[rt].fi=(t[rt].se>0)?l:0;
		return;
	}
	int mid=(l+r)>>1;
	if(u<=mid) update(ls[rt],l,mid,u,v);
	else update(rs[rt],mid+1,r,u,v);
	pushup(rt);
}
int query(int rt,int l,int r,int u)
{
	if(!rt) return 0;
	if(l==r) return t[rt].se;
	int mid=(l+r)>>1;
	if(u<=mid) return query(ls[rt],l,mid,u);
	else return query(rs[rt],mid+1,r,u);
}
int merge(int x,int y,int l,int r)
{
	if(!x || !y) return x+y;
	if(l==r)
	{
		t[x].se+=t[y].se;
		t[x].fi=(t[x].se>0)?l:0;
		return x;
	}
	int mid=(l+r)>>1;
	ls[x]=merge(ls[x],ls[y],l,mid);
	rs[x]=merge(rs[x],rs[y],mid+1,r);
	pushup(x);
	return x;
}
inline void add(int x,int y)
{
	v[++tot]=y;
	if(he[x]==0) he[x]=tot;
	pr[tot]=ta[x];
	su[ta[x]]=tot;
	ta[x]=tot;
}
inline void del(int x)
{
	ta[x]=pr[ta[x]];
	su[ta[x]]=0;
	if(ta[x]==0) he[x]=0;
}
int main()
{
	freopen("major.in","r",stdin);
	freopen("major.out","w",stdout);
	read(n),read(q);
	for(int i=1;i<=n;i++)
	{
		read(siz[i]);
		for(int j=0,x;j<siz[i];j++)
		{
			read(x),add(i,x);
			update(rt[i],1,n+q,x,1);
		}
	}
	int op,x,y,z; pii ans;
	for(int Case=1;Case<=q;Case++)
	{
		read(op);
		if(op==1)
		{
			read(x),read(y);
			siz[x]++,add(x,y);
			update(rt[x],1,n+q,y,1);
		}
		if(op==2)
		{
			read(x);
			update(rt[x],1,n+q,v[ta[x]],-1);
			siz[x]--,del(x);
		}
		if(op==3)
		{
			read(x); ans=pii(0,0),y=z=0;
			for(int i=1;i<=x;i++)
				read(a[i]),ans=ans+t[rt[a[i]]],y+=siz[a[i]];
			if(ans.fi==0) { print(-1),pc('\n'); continue; }
			for(int i=1;i<=x;i++)
				z+=query(rt[a[i]],1,n+q,ans.fi);
			if(z>y/2) print(ans.fi);
			else print(-1);
			pc('\n');
		}
		if(op==4)
		{
			read(x),read(y),read(z);
			if(siz[x]==0)
			{
				he[z]=he[y],ta[z]=ta[y];
				rt[z]=rt[y],siz[z]=siz[y];
			}
			else if(siz[y]==0)
			{
				he[z]=he[x],ta[z]=ta[x];
				rt[z]=rt[x],siz[z]=siz[x];
			}
			else
			{
				rt[z]=merge(rt[x],rt[y],1,n+q);
				su[ta[x]]=he[y];
				pr[he[y]]=ta[x];
				he[z]=he[x],ta[z]=ta[y];
				siz[z]=siz[x]+siz[y];
			}
		}
	}
	return 0;
}

「NOI2022」挑戰 NPC Ⅱ

暴力=正解,笑死。

首先有一個暴力的想法。因爲只有 \(k\) 個點不一樣,考慮從上往下匹配,兒子能匹配就匹配,最後剩下不超過 \(k\) 個兒子。暴力匹配即可。

爲什麼這是對的?

比如匹配兩棵樹,若有 \(t\) 個兒子,那麼接下來兩棵樹節點數之差就變成原來減去 \(t-1\),那麼就變成 \(x_1+x_2+…+x_m=k, (x_1+1)(x_2+1)…(x_m+1)\) 最大是多少的問題了,可以說明最大應該是 \(2^k\),但其實根本跑不滿。理論上界 \(O(n2^kk!)\)

點擊查看代碼
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
#define fi first
#define se second
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
template <class T>
inline void read(T &x)
{
	x=0; int f=1; char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
	x=(f==1)?x:-x;
}
const int maxn=100005;
struct Tree
{
	const ull base=998244353;
	vector<int> g[maxn];
	int n,rt,fa[maxn],siz[maxn]; ull mi[maxn],f[maxn];
	void dfs(int x)
	{
		f[x]=1,siz[x]=1;
		for(auto y:g[x])
			dfs(y),f[x]+=f[y]*mi[siz[y]],siz[x]+=siz[y];
	}
	void init()
	{
		read(n);
		FOR(i,1,n)
		{
			read(fa[i]);
			if(fa[i]==-1) rt=i;
			else g[fa[i]].push_back(i);
		}
		mi[0]=1;
		FOR(i,1,n) mi[i]=mi[i-1]*base;
		dfs(rt);
	}
	void reset()
	{
		FOR(i,1,n) g[i].clear();
	}
}G,H;
int C,T,k;
int check(int u,int v)
{
	if(u==0 || v==0) return 1;
	if(abs(G.siz[u]-H.siz[v])>k) return 0;
	vector<int> a,b; int j=0;
	for(int i=0;i<G.g[u].size();i++)
	{
		for(;j<H.g[v].size() && G.f[G.g[u][i]]>H.f[H.g[v][j]];j++)
			b.push_back(H.g[v][j]);
		if(j>=H.g[v].size() || G.f[G.g[u][i]]!=H.f[H.g[v][j]]) a.push_back(G.g[u][i]);
		else j++;
	}
	for(;j<H.g[v].size();j++) b.push_back(H.g[v][j]);
	if(a.size()>k || b.size()>k || a.size()<b.size()) return 0;
	if(a.size()==0) return 1;
	while(b.size()<a.size()) b.push_back(0);
	int e[5][5],p[5];
	FOR(i,0,(int)a.size()-1)
		FOR(j,0,(int)b.size()-1)
			e[i][j]=check(a[i],b[j]);
	FOR(i,0,(int)a.size()-1) p[i]=i;
	do
	{
		int fl=1;
		FOR(i,0,(int)a.size()-1) fl&=e[i][p[i]];
		if(fl) return 1;
	}while(next_permutation(p,p+a.size()));
	return 0;
}
inline bool cmp1(const int &x,const int &y) { return G.f[x]<G.f[y]; }
inline bool cmp2(const int &x,const int &y) { return H.f[x]<H.f[y]; }
void solve()
{
	G.init(),H.init();
	FOR(i,1,G.n) sort(G.g[i].begin(),G.g[i].end(),cmp1);
	FOR(i,1,H.n) sort(H.g[i].begin(),H.g[i].end(),cmp2);
	if(check(G.rt,H.rt)) puts("Yes");
	else puts("No");
	G.reset(),H.reset();
}
int main()
{
	freopen("iso.in","r",stdin);
	freopen("iso.out","w",stdout);
	read(C),read(T),read(k);
	while(T--) solve();
	return 0;
}

「NOI2022」冒泡排序

磨了 5h,磨出來了。

題解直接看洛谷好了。

爲什麼要 \(l\) 從大往小排:這樣纔好貪心。

時間 \(O(n\log n)\)

點擊查看代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define fi first
#define se second
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define PER(i,a,b) for(int i=(a);i>=(b);i--)
template <class T>
inline void read(T &x)
{
	x=0; int f=1; char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
	x=(f==1)?x:-x;
}
#define pc putchar
template <class T>
void print(T x)
{
	if(x<0) pc('-'),x=-x;
	if(x>9) print(x/10);
	pc(x%10+'0');
}
const int maxn=1000005;
const int inf=0x3f3f3f3f;
int n,m,mx,a[maxn],b[maxn];
struct Query { int l,r,v; } q[maxn];
inline bool cmp(const Query &a,const Query &b)
{
	if(a.v!=b.v) return a.v>b.v;
	return a.l>b.l;
}
#define ls (rt<<1)
#define rs (rt<<1|1)
struct Segment_Tree_1
{
	int p[maxn<<2],mn[maxn<<2],mn_id[maxn<<2],tag[maxn<<2];
	inline void pushup(int rt)
	{
		mn[rt]=min(mn[ls],mn[rs]);
		mn_id[rt]=(mn_id[ls]>0)?mn_id[ls]:mn_id[rs];
	}
	inline void pushtag(int rt,int z) { mn[rt]=inf,mn_id[rt]=0,p[rt]=max(p[rt],z),tag[rt]=max(tag[rt],z); }
	inline void pushdown(int rt) { if(tag[rt]) pushtag(ls,tag[rt]),pushtag(rs,tag[rt]),tag[rt]=0; }
	void build(int rt,int l,int r)
	{
		mn[rt]=inf,mn_id[rt]=p[rt]=tag[rt]=0;
		if(l==r) { mn_id[rt]=l; return; }
		int mid=(l+r)>>1;
		build(ls,l,mid),build(rs,mid+1,r),pushup(rt);
	}
	void update(int rt,int l,int r,int x,int y,int z)
	{
		if(x<=l && r<=y) { pushtag(rt,z); return; }
		pushdown(rt);
		int mid=(l+r)>>1;
		if(x<=mid) update(ls,l,mid,x,y,z);
		if(y>mid) update(rs,mid+1,r,x,y,z);
		pushup(rt);
	}
	void modify(int rt,int l,int r,int u,int v)
	{
		if(l==r) { mn[rt]=v,mn_id[rt]=0; return; }
		pushdown(rt);
		int mid=(l+r)>>1;
		if(u<=mid) modify(ls,l,mid,u,v);
		else modify(rs,mid+1,r,u,v);
		pushup(rt);
	}
	int query(int rt,int l,int r,int x,int y)
	{
		if(x<=l && r<=y) return mn[rt];
		pushdown(rt);
		int mid=(l+r)>>1;
		if(y<=mid) return query(ls,l,mid,x,y);
		if(x>mid) return query(rs,mid+1,r,x,y);
		return min(query(ls,l,mid,x,y),query(rs,mid+1,r,x,y));
	}
	int query_id(int rt,int l,int r,int x,int y)
	{
		if(x<=l && r<=y) return mn_id[rt];
		pushdown(rt);
		int mid=(l+r)>>1;
		if(y<=mid) return query_id(ls,l,mid,x,y);
		if(x>mid) return query_id(rs,mid+1,r,x,y);
		int ans=query_id(ls,l,mid,x,y);
		if(ans>0) return ans;
		return query_id(rs,mid+1,r,x,y);
	}
	void get_b(int rt,int l,int r)
	{
		if(l==r) { b[l]=p[rt]; return; }
		pushdown(rt);
		int mid=(l+r)>>1;
		get_b(ls,l,mid),get_b(rs,mid+1,r);
	}
}T1;
struct Segment_Tree_2
{
	int add[maxn<<2]; pii mn[maxn<<2];
	inline pii cmp(const pii &a,const pii &b) { return (a.fi<=b.fi)?a:b; }
	inline void pushup(int rt) { mn[rt]=cmp(mn[ls],mn[rs]); }
	inline void pushtag(int rt,int z) { mn[rt].fi+=z,add[rt]+=z; }
	inline void pushdown(int rt) { if(add[rt]) pushtag(ls,add[rt]),pushtag(rs,add[rt]),add[rt]=0; }
	void build(int rt,int l,int r)
	{
		mn[rt].fi=mn[rt].se=add[rt]=0;
		if(l==r) { mn[rt].se=l; return; }
		int mid=(l+r)>>1;
		build(ls,l,mid),build(rs,mid+1,r),pushup(rt);
	}
	void update(int rt,int l,int r,int x,int y,int z)
	{
		if(x>y) return;
		if(x<=l && r<=y) { pushtag(rt,z); return; }
		pushdown(rt);
		int mid=(l+r)>>1;
		if(x<=mid) update(ls,l,mid,x,y,z);
		if(y>mid) update(rs,mid+1,r,x,y,z);
		pushup(rt);
	}
	pii query(int rt,int l,int r,int x,int y)
	{
		if(x<=l && r<=y) return mn[rt];
		pushdown(rt);
		int mid=(l+r)>>1;
		if(y<=mid) return query(ls,l,mid,x,y);
		if(x>mid) return query(rs,mid+1,r,x,y);
		return cmp(query(ls,l,mid,x,y),query(rs,mid+1,r,x,y));
	}
}T2;
struct BIT
{
	int tr[maxn];
	#define lowbit(x) ((x)&(-(x)))
	inline void add(int x,int y) { for(;x<=mx;x+=lowbit(x)) tr[x]+=y; }
	inline void update(int l,int r,int x) { if(l>r) return; add(l,x),add(r+1,-x); }
	inline int query(int x)
	{
		int ans=0;
		for(;x;x-=lowbit(x)) ans+=tr[x];
		return ans;
	}
}T3;
void solve()
{
	read(n),read(m);
	vector<int> cur;
	FOR(i,1,m) read(q[i].l),read(q[i].r),read(q[i].v),cur.push_back(q[i].v);
	sort(cur.begin(),cur.end());
	cur.erase(unique(cur.begin(),cur.end()),cur.end());
	FOR(i,1,m) q[i].v=lower_bound(cur.begin(),cur.end(),q[i].v)-cur.begin()+1;
	sort(q+1,q+m+1,cmp);
	FOR(i,1,n) a[i]=0;
	mx=cur.size();
	T1.build(1,1,n);
	for(int l=1,r=0;l<=m;l=r+1)
	{
		r=l;
		for(;r<m && q[r].v==q[r+1].v;r++);
		FOR(i,l,r)
		{
			if(T1.query(1,1,n,q[i].l,q[i].r)==q[i].v) continue;
			int k=T1.query_id(1,1,n,q[i].l,q[i].r);
			if(!k) { puts("-1"); return; }
			a[k]=q[i].v,T1.modify(1,1,n,k,q[i].v);
		}
		FOR(i,l,r) T1.update(1,1,n,q[i].l,q[i].r,q[i].v);
	}
	T1.get_b(1,1,n);
	T2.build(1,1,mx);
	FOR(i,1,n)
		if(a[i]>0) T2.update(1,1,mx,a[i]+1,mx,1);
	for(int i=1;i<=n;i++)
		if(a[i]==0)
		{
			pii ans=T2.query(1,1,mx,max(b[i],1),mx);
			a[i]=ans.se,T2.update(1,1,mx,1,a[i]-1,1);
		}
		else T2.update(1,1,mx,a[i]+1,mx,-1),T2.update(1,1,mx,1,a[i]-1,1);
	FOR(i,1,mx) T3.tr[i]=0;
	ll ans=0;
	PER(i,n,1)
		ans+=T3.query(a[i]-1),T3.add(a[i],1);
	printf("%lld\n",ans);
}
int main()
{
	freopen("bubble.in","r",stdin);
	freopen("bubble.out","w",stdout);
	int T;
	read(T);
	while(T--) solve();
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章