2022NOIP A层联测15 AB C D

【规律/数学】T1:给你值域在\([1,2^n]\)之间的数,要求你把他们分2组,使得每组\(sigma(ai^p),0<=p<n\)相等,输出构造方案。

考场

打表找规律,发现以4为一组对称构造就合法。

正解

不知道正解,但是考场没有删除调试!低错爆零!

点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define rint register int
#define chu printf
#define ulll unsigned __int128
inline ll re() {
    ll x = 0, h = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            h = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * h;
}
const int N = (1 << 20) + 100;
bool vs[N];   //组是什么品种
bool chs[N];  //选不选
int one[N], two[N];
int n, k;
inline ll qpow(ll ar, ll br) {
    ll cr = 1;
    while (br) {
        if (br & 1)
            cr = cr * ar;
        ar = ar * ar;
        br >>= 1;
    }
    return cr;
}
inline void Group(int l, int r)  //现在编号[l,r]区间
{
    // chu("group:%d--%d\n",l,r);
	if(r>(1<<13))
	{
		chu("shit\n");exit(0);
	}
    int mid = (l + r) >> 1;
    for (rint i = 1; i <= mid; ++i) vs[i + mid] = !vs[i];
    if (r == (1 << 13))  //到边界了
        return;
    Group(l, r * 2);
}
inline void Divide(int l, int r) {
    int ir = l;
    // chu("ir:%d  bj:%d\n",ir,ir*8);
    if (vs[l]) {
        chs[8 * ir - 7] = chs[8 * ir - 4] = chs[8 * ir - 2] = chs[8 * ir - 1] = 1;
    } else {
        for (rint i = 8 * ir - 7; i <= 8 * ir; ++i) chs[i] = 1;
        chs[8 * ir - 7] = chs[8 * ir - 4] = chs[8 * ir - 2] = chs[8 * ir - 1] = 0;
    }
}
int main() {
    // freopen("1.in","r",stdin);
    // freopen("1.out","w",stdout);
  freopen("sandalphon.in","r",stdin);
   freopen("sandalphon.out","w",stdout);
    n = re();
    k = re();
    // chu("%d %d\n",n,k);
    if (n == 1) {
        chu("1\n");
        chu("1 1\n");
        chu("1 2");
        return 0;
    }
    if (n == 2) {
        chu("1\n");
        chu("2 1 4\n");
        chu("2 2 3");
        return 0;
    }
    vs[1] = 1;  // 1是分合
    Group(1, 2);
    // chu("dont out\n");
    for (rint i = 1; i <= (1 << 13); ++i) Divide(i, i);
    chu("1\n");
	//for(rint i=1;i<=10;++i)chu("%d ",chs[i]);chu("\n");
    for (rint i = 1; i <= (1 << n); ++i)
        if (chs[i])
            one[++one[0]] = i;
        else
            two[++two[0]] = i;
    chu("%d ", one[0]);
	//for(rint i=1;i<=10;++i)chu("%d ",vs[i]);chu("\n");
    for (rint i = 1; i <= one[0]; ++i) chu("%d ", one[i]);
    chu("\n");
    chu("%d ", two[0]);
    for (rint i = 1; i <= two[0]; ++i) chu("%d ", two[i]);
    chu("\n");
    return 0;
}
/*

*/

【交互/图论/二分】T2:给你点数是n的图,你可以进行<=1e5次询问,询问边两端都在集合S中的边数,求最后图的边信息。(n<=2000)

考场

n^2枚举2个点有没有边

正解

固定一个点i,求·[1,i-1]中和它连的边,考虑快速求出,可以二分区间中和i连的点位置,因为[l,r]check(l,mid)=0,可以把l边界缩小到mid+1,这就是单调性了\(O(2*m*logn)\),还是比较玄乎,考虑[1,i-1]的点边信息已经知道,维护前缀和就可以省略一次询问,-->\(m*logn\)

点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
#include "YuukiAsuna.h"
using namespace std;
#define ll long long
#define ull unsigned long long
#define rint register int
#define chu printf
inline ll re() {
    ll x = 0, h = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            h = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * h;
}
const int N = 2000 + 100;
vector<pair<int,int> >ans;
vector<int>s;
bool mp[N][N];
int sum[N][N];
inline int Calc(int x,int y,int der)//找der在(x,y)集合的连边数量
{
	if(x==y)
	{
		s.push_back(x);s.push_back(der);
		int du=Query(s);
		s.pop_back();s.pop_back();
		return du;
	}
	for(rint i=x;i<=y;++i)s.push_back(i);
	s.push_back(der);
	int du=Query(s);
	du=du-(sum[y][y]-sum[y][x-1]);
	for(rint i=x;i<=y+1;++i)s.pop_back();
	return du;
}
vector<pair<int, int> > Asuna(int n, int Q)
{
    for(rint i=2;i<=n;++i)
	{
		int last=1,l,r;
		int deg=Calc(1,i-1,i);//
		for(rint j=1;j<=deg;++j)
		{
			l=last,r=i-1;
			int nas=0;
			while(l<=r)
			{
				int mid=(l+r)>>1;
				if(Calc(last,mid,i))r=mid-1,nas=mid;
				else l=mid+1;
			}
			mp[nas][i]=1;
			last=nas+1;
		}
		int sp=0;
		for(rint j=1;j<=i;++j)
		{
			sp+=mp[j][i];
			sum[i][j]=sum[i-1][j]+sp;
		}
		sum[i][i]=sum[i-1][i-1]+sp;
	}
	for(rint i=1;i<=n;++i)
	for(rint j=i+1;j<=n;++j)if(mp[i][j])ans.push_back(make_pair(i,j));
	return ans;
}
/*
然后就是
*/

【数据结构:线段树维护区间和+覆盖性懒惰标记(渐进思想)】T3:给出一个实数序列A,opt=1:把[l,r]序列的数变成v;opt=2,把[l,r]取cos;opt=3,询问[l,r]区间和。(n,m<=3e5)

暴力(80tps)

cos进行>=45次趋近一个定值,所以记录lim是一个区间最少cos次数,如果>=45就不管它了;其他覆盖正常。
注意保证复杂度的优化【1】如果取cos时,已经有区间覆盖的标记直接变就行,不继续【2】cos更新必须彻底到叶子除非遇到【1】
\(O(m*logn*k)\),因为cos更新的复杂度不对。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define rint register int
#define chu printf
inline ll re()
{
	ll x=0,h=1;char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')h=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*h;
}
const int N=3e5+100;
double a[N];
int n,m;
struct Segment_tree
{
	#define lson (rt<<1)
	#define rson (rt<<1|1)
	double val[N<<2],tag[N<<2];//区间和,覆盖懒惰标记
	int lim[N<<2];//迭代次数限制
	inline void update(int rt,int l,int r,double cover,int dfs)
	{
		val[rt]=(r-l+1)*cover;
		tag[rt]=cover;
		lim[rt]=dfs;
	}
	inline void pushdown(int rt,int l,int mid,int r)
	{
		if(tag[rt]==1e9)return;
		tag[lson]=tag[rson]=tag[rt];
		val[lson]=(mid-l+1)*tag[rt];
		val[rson]=(r-(mid+1)+1)*tag[rt];
		lim[lson]=lim[rson]=lim[rt];//为什么?lim也需要从上到下,不然的话
		tag[rt]=1e9;
	}
	inline void pushup(int rt)
	{
		val[rt]=val[lson]+val[rson];
		lim[rt]=min(lim[lson],lim[rson]);
	}
	inline void build(int rt,int l,int r)
	{
		//chu("buildok:%d--%d\n",l,r);
		tag[rt]=1e9;
		if(l==r)
		{
			val[rt]=a[l];
			lim[rt]=0;
			return;
		}
		int mid=(l+r)>>1;
		build(lson,l,mid);
		build(rson,mid+1,r);
		pushup(rt);
	}
	inline void insert_val(int rt,int l,int r,int L,int R,double d)
	{
		if(L<=l&&r<=R)
		{
			update(rt,l,r,d,0);
			return;
		}
		int mid=(l+r)>>1;
		pushdown(rt,l,mid,r);
		if(L<=mid)insert_val(lson,l,mid,L,R,d);
		if(R>mid)insert_val(rson,mid+1,r,L,R,d);
		pushup(rt);
	}
	inline void insert_cos(int rt,int l,int r,int L,int R)
	{
		if(lim[rt]>=45)return;
		if(L<=l&&r<=R&&tag[rt]!=1e9)
		{
			update(rt,l,r,cos(tag[rt]),lim[rt]+1);//原来有跌算
			return;
		}
		if(l==r)
		{
			val[rt]=cos(val[rt]);
			lim[rt]++;
			return;
		}
		int mid=(l+r)>>1;
		pushdown(rt,l,mid,r);
		if(L<=mid)insert_cos(lson,l,mid,L,R);
		if(R>mid)insert_cos(rson,mid+1,r,L,R);
		pushup(rt);
	}
	inline double query(int rt,int l,int r,int L,int R)
	{
		if(L<=l&&r<=R)return val[rt];
		int mid=(l+r)>>1;
		double ret=0;
		pushdown(rt,l,mid,r);
		if(L<=mid)ret+=query(lson,l,mid,L,R);
		if(R>mid)ret+=query(rson,mid+1,r,L,R);
		return ret;
	}
}t;
int main()
{
	 freopen("excalibur.in","r",stdin);
	 freopen("excalibur.out","w",stdout);
	// freopen("1.in","r",stdin);
	// freopen("1.out","w",stdout);
	n=re(),m=re();
	for(rint i=1;i<=n;++i)scanf("%lf",&a[i]);
	t.build(1,1,n);
	for(rint i=1;i<=m;++i)
	{
		int opt=re();
		if(opt==3)
		{
			int l=re(),r=re();double var;
			scanf("%lf",&var);
			t.insert_val(1,1,n,l,r,var);
		}
		else if(opt==1)
		{
			int l=re(),r=re();
			t.insert_cos(1,1,n,l,r);
		}
		else
		{
			int l=re(),r=re();
			chu("%.13lf\n",t.query(1,1,n,l,r));
		}
	}
	return 0;
}
/*

*/

trie树小专题

【trie树贪心/二分】T4:给出一个n个点的图,要求你连n条边,每条边的权值时2端点的异或值,求最小边权最大。(n<=5e5)

正解

发现一定时22配对不会失去最优解(已有方案删除边不会让答案更劣);
配对:二分答案,考虑每一位上的1能不能加上-->按照最优配对能不能不剩下点。
如果now位=1,方案唯一;
now位是0,贪心得让0和1配对,这样后面就没有要求了,可以随便消除,剩下不得不00或11的再配对。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define rint register int
#define chu printf
inline ll re()
{
	ll x=0,h=1;char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')h=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*h;
}
const int N=5e5+100;
int ch[N*31][2],tot=1;
int n,a[N],siz[N*31];
#define ls(rt)  ch[rt][0]
#define rs(rt)  ch[rt][1]
inline int solve(int u,int v,int dep,int lim)
{
	if(!u||!v)return siz[u]+siz[v];
	if(dep<0)return 0;
	//如果这一位要求是1
	if((lim>>dep)&1)
	{
		if(dep==0)
		return abs(siz[ls(u)]-siz[rs(v)])+abs(siz[ls(v)]-siz[rs(u)]);
		return solve(ls(u),rs(v),dep-1,lim)+solve(ls(v),rs(u),dep-1,lim);
	}
	if(siz[ls(u)]>siz[rs(v)]&&siz[rs(u)]>siz[ls(v)])return siz[u]-siz[v];
	if(siz[ls(u)]<=siz[rs(v)]&&siz[rs(u)]<=siz[ls(v)])return siz[v]-siz[u];
	int tot0=siz[ls(u)]+siz[ls(v)],tot1=siz[rs(u)]+siz[rs(v)];
	if(tot0<tot1)
	{
		int w=solve(rs(u),rs(v),dep-1,lim);
		return max(w-tot0,abs(siz[u]-siz[v]));
	}
	else
	{
		int w=solve(ls(u),ls(v),dep-1,lim);
		return max(w-tot1,abs(siz[u]-siz[v]));
	}
}
int main()
{
	 freopen("win.in","r",stdin);
	 freopen("win.out","w",stdout);
	// freopen("1.in","r",stdin);
	// freopen("1.out","w",stdout);
	n=re();
	for(rint i=1;i<=n;++i)
	{
		a[i]=re();
		int now=1;
		for(rint j=30;j>=0;--j)
		{
			siz[now]++;
			bool chr=(a[i]>>j)&1;
			if(ch[now][chr]==0)ch[now][chr]=++tot;
			now=ch[now][chr];
		}
		siz[now]++;
	}
	int ans=0;
	for(rint i=30;i>=0;--i)
	{
		if(solve(1,1,30,ans+(1<<i))==0)ans=ans+(1<<i);
	}
	chu("%d",ans);
	return 0;
}
/*

*/

【trie树:最小生成树/神仙pushup结合预处理】模板:求给出的序列22异或最小值,只有不同颜色的可以异或,而且带修改。

【1】生成树:在dfs你的trie树模拟找联通块的边(最小边权)过程,启发式合并把节点集合传递到根。

点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define rint register int
#define chu printf
inline ll re() {
    ll x = 0, h = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            h = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * h;
}
const int N = 2e5 + 100;
int tr[N * 30][2], n, m, a[N], cor[N], store[N * 30], tot;  //值和颜色
vector<int> contain[N * 30];                                //记录每个节点内部联通点集
vector<int> g[N];                                           //记录生成树
multiset<int> s;
inline void add_dot(int x, int y) {
    if (!x || !y)
        return;
    g[x].push_back(y);
    g[y].push_back(x);
    // chu("pos:%d  %d:ins:%d %d\n",x,y,a[x],a[y]);
    if (cor[x] != cor[y])
        s.insert(a[x] ^ a[y]);
}
inline void ins_trie(int x, int id) {
    int now = 1;
    // chu("insert:%d %d\n",x,id);
    for (rint i = 29; i >= 0; --i) {
        bool ch = ((x >> i) & 1);
        if (!tr[now][ch])
            tr[now][ch] = ++tot;
        now = tr[now][ch];
    }
    if (store[now])
        add_dot(id, store[now]);
    store[now] = id;
}
inline int find_trie(int x) {
    int now = 1;
    for (rint i = 29; i >= 0; --i) {
        bool chr = (x >> i) & 1;
        if (tr[now][chr])
            now = tr[now][chr];
        else
            now = tr[now][!chr];
    }
    return store[now];
}
inline void dfs_trie(int now, int pos) {
    if (tr[now][0])
        dfs_trie(tr[now][0], pos - 1);
    if (tr[now][1])
        dfs_trie(tr[now][1], pos - 1);
    if (!tr[now][0] && !tr[now][1]) {
        contain[now].push_back(store[now]);
        return;
    }
    if (!tr[now][0] || !tr[now][1]) {
        if (!tr[now][0])
            swap(contain[now], contain[tr[now][1]]);
        if (!tr[now][1])
            swap(contain[now], contain[tr[now][0]]);
        return;
    }
    if (contain[tr[now][0]].size() > contain[tr[now][1]].size())
        swap(contain[tr[now][0]], contain[tr[now][1]]);
    int res1 = 0, res2 = 0, Min = 2147483647;
    for (rint i = 0; i < contain[tr[now][0]].size(); ++i) {
        int u = contain[tr[now][0]][i];
        int v = find_trie(a[u] ^ (1 << pos));
        if (v && Min > (a[u] ^ a[v]))  //连最小的边
        {
            Min = a[u] ^ a[v];
            res1 = u, res2 = v;
        }
    }
    add_dot(res1, res2);                      //启发合并
    swap(contain[now], contain[tr[now][1]]);  // g该放的都放了啊
    for (rint i = 0; i < contain[tr[now][0]].size(); ++i) {
        int u = contain[tr[now][0]][i];
        contain[now].push_back(u);
    }
}
int main() {
    freopen("who.in", "r", stdin);
    freopen("who.out", "w", stdout);
    //	freopen("who.in","r",stdin);
    //	freopen("who.out","w",stdout);
    tot = 1;
    n = re(), m = re();
    for (rint i = 1; i <= n; ++i) a[i] = re();
    for (rint i = 1; i <= n; ++i) cor[i] = re();
    for (rint i = 1; i <= n; ++i) ins_trie(a[i], i);
    dfs_trie(1, 29);  //默认1是虚点,不然怕出问题
                      // chu("insert:%d\n",s.size());
    for (rint i = 1; i <= m; ++i) {
        int x = re(), y = re();
        if (cor[x] != y)  //如果颜色真的改了
        {
            for (rint j = 0; j < g[x].size(); ++j) {
                int connect = g[x][j];
                if (cor[connect] == cor[x])  //现在不一样了
                {
                    s.insert(a[connect] ^ a[x]);
                }
                if (cor[connect] == y) {
                    s.erase(s.find(a[connect] ^ a[x]));
                }
            }
        }
        cor[x] = y;
        chu("%d\n", *s.begin());
    }
    return 0;
}
/*
5 2
1 2 3 4 5
1 1 1 2 1
4 3
5 3

*/

【2】预处理:直接把答案在trie树维护,叶子节点记录颜色和出现次数,map映射,修改一条路径的值,维护mini

点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
#define fre(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout)
#define Rep(i, a, b) for (int i = a; i <= b; ++i)
#define Dwn(i, a, b) for (int i = a; i >= b; --i)
#define pii pair<int, int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;

const int maxn = 2e5 + 10, INF = 2e9, B = 30;
char buf[1 << 20], *p1, *p2;
char gc() { return p1 == p2 && (p2 = buf + fread(p1 = buf, 1, 1 << 20, stdin), p1 == p2) ? EOF : *p1++; }
int read() {
    int x = 0;
    bool f = 0;
    char c = gc();
    while (!isdigit(c)) {
        if (c == '-')
            f = 1;
        c = gc();
    }
    while (isdigit(c)) x = x * 10 + (c ^ 48), c = gc();
    return f ? -x : x;
}
int n, m;
int a[maxn];
int col[maxn];
bool fg;
map<int, int> Map[maxn];

struct Te {
#define lch ch[u][0]
#define rch ch[u][1]
    int ch[maxn * 80][2];
    int id[maxn * 80];
    int val[maxn * 80], mini[maxn * 80];
    bool same[maxn * 80];
    int color[maxn * 80];
    int tot;
    int root;
    Te() {
        tot = 1;
        root = 1;
        memset(val, 0x3f, sizeof(val)), memset(mini, 0x3f, sizeof(mini));
    }
    void Pushup(int u) {
        if (!lch) {
            if (same[rch])
                same[u] = true, color[u] = color[rch], mini[u] = INF;
            else
                mini[u] = mini[rch], same[u] = false;
            return;
        }
        if (!rch) {
            if (same[lch])
                same[u] = true, color[u] = color[lch], mini[u] = INF;
            else
                mini[u] = mini[lch], same[u] = false;
            return;
        }
        if (same[lch] && same[rch]) {
            if (color[lch] == color[rch]) {
                same[u] = true;
                color[u] = color[lch];
            } else
                same[u] = false, mini[u] = val[u];
            return;
        }
        same[u] = false;
        mini[u] = INF;
        if (!same[lch])
            mini[u] = min(mini[u], mini[lch]);
        if (!same[rch])
            mini[u] = min(mini[u], mini[rch]);
    }
    int Min(int u, int x, int k) {
        if (!u)
            return INF;
        int res = 0;
        for (int i = k; i >= 0; --i) {
            int c = (x >> i) & 1;
            if (ch[u][c])
                u = ch[u][c];
            else
                u = ch[u][c ^ 1], res |= 1 << i;
        }
        return res;
    }
    void Insert(int x) {
        int u = root;
        Dwn(i, B, 0) {
            int c = (x >> i) & 1;
            if (!ch[u][c])
                ch[u][c] = ++tot;
            if (c) {
                if (i)
                    val[u] = min(val[u], Min(ch[u][c ^ 1], x, i - 1) | (1 << i));
                else
                    val[u] = ch[u][c ^ 1] ? 1 : INF;
            }
            u = ch[u][c];
        }
        if (!id[u])
            id[u] = ++id[0];
    }
    void Add(int x, int cls) {
        int u = root;
        vector<int> vec;
        Dwn(i, B, 0) {
            vec.emplace_back(u);
            int c = (x >> i) & 1;
            u = ch[u][c];
        }
        Map[id[u]][cls]++;
        if (Map[id[u]].size() == 1) {
            same[u] = true;
            color[u] = (*Map[id[u]].begin()).fir;
        } else {
            same[u] = false;
            mini[u] = 0;
        }
        reverse(vec.begin(), vec.end());
        for (auto p : vec) Pushup(p);
    }
    void Del(int x, int cls) {
        int u = root;
        vector<int> vec;
        Dwn(i, B, 0) {
            vec.emplace_back(u);
            int c = (x >> i) & 1;
            u = ch[u][c];
        }
        Map[id[u]][cls]--;
        if (Map[id[u]][cls] == 0)
            Map[id[u]].erase(cls);
        if (Map[id[u]].size() == 1) {
            same[u] = true;
            color[u] = (*Map[id[u]].begin()).fir;
        } else {
            same[u] = false;
            mini[u] = 0;
        }
        reverse(vec.begin(), vec.end());
        for (auto p : vec) Pushup(p);
    }
    void Pre(int u) {
        if (lch)
            Pre(lch);
        if (rch)
            Pre(rch);
        if (lch || rch)
            Pushup(u);
    }
    void Modify(int x, int np, int nw) {
        int u = root;
        vector<int> vec;
        Dwn(i, B, 0) {
            vec.emplace_back(u);
            int c = (x >> i) & 1;
            u = ch[u][c];
        }
        Map[id[u]][np]--;
        if (Map[id[u]][np] == 0)
            Map[id[u]].erase(np);
        Map[id[u]][nw]++;
        if (Map[id[u]].size() == 1) {
            same[u] = true;
            color[u] = (*Map[id[u]].begin()).fir;
        } else {
            same[u] = false;
            mini[u] = 0;
        }
        reverse(vec.begin(), vec.end());
        for (auto p : vec) Pushup(p);
    }
} T;

void solve() {
    fre(who);
    n = read(), m = read();
    Rep(i, 1, n) a[i] = read();
    Rep(i, 1, n) col[i] = read();
    Rep(i, 1, n) T.Insert(a[i]);
    Rep(i, 1, n) T.Add(a[i], col[i]);
    // T.Pre(T.root);
    while (m--) {
        int x, y;
        x = read(), y = read();
        T.Modify(a[x], col[x], y);
        col[x] = y;
        printf("%d\n", T.mini[T.root]);
    }
}

int main() { return solve(), 0; }

【trie树贪心构造】https://www.luogu.com.cn/problem/CF1446C

把节点加入trie树,从叶子合并到根,维护函数\(f[rt]\)表示到rt子树内的合法的最多保留点,如果对于第k位只有一个位有值直接递归;否则对于lson和rson取max+1:因为lson保留>1那么rson保留最多1,这样不会让答案更多,所以保留一个不失去最优,再从!k位选一个一定不会增加互为相同。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define rint register int 
#define ll long long
#define ull unsigned long long
#define chu printf
inline ll re()
{
    ll x=0,h=1;char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
const int N=2e5+100;
int n,a[N];
int tr[N*30][2],tot=1;//2^29就够了
inline void ins(int x)
{
    int now=1;
    for(rint i=29;i>=0;--i)
    {
        bool ch=(x>>i)&1;
        if(!tr[now][ch])tr[now][ch]=++tot;
        now=tr[now][ch];
    }
}
inline int del(int rt)
{
    if(!tr[rt][0]&&!tr[rt][1])return 1;
    if(!tr[rt][0])return del(tr[rt][1]);
    if(!tr[rt][1])return del(tr[rt][0]);
    return max(del(tr[rt][1]),del(tr[rt][0]))+1;
}
int main()
{
    //("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    n=re();
    for(rint i=1;i<=n;++i)
    a[i]=re();
    for(rint i=1;i<=n;++i)
    {
        ins(a[i]);
    }
    chu("%d",n-del(1));
    return 0;

}
/*
5
0 1 5 2 6

7
6 9 8 7 3 5 2
*/

trie树贪心一般从低到高递归选择处理

posted on 2022-10-25 18:08  HZOI-曹蓉  阅读(19)  评论(0编辑  收藏  举报