MX 暑假集训 7.21

命に嫌われている。 / まふまふ【歌ってみた】

省流:\(100+100+35+100\)

神秘大样例。

T1 两个大样例总共给了 \(7\) 行。

T2 序列题,答案级别为逆序对数,大样例答案 \(0,1,1,0\),其中第一个和第四个大样例 \(n=1\)

T3 字符串题,\(1\le T\le 10^5\),字符串长度 \(2\times 10^6\),结果最大的样例在题面内,字符串长度最长为 \(9\),答案最大为 \(2\)

T4 树论,大样例给了一个菊花和一个链,然后输出了一个数加上 \(n-1\)\(0\)

T1: Akari

题意

给定一个 \(R\times C\) 的网格图,图上有 \(n\) 个灯和 \(m\) 个障碍,保证每个网格上不会同时存在灯和障碍,灯会向上下左右四个方向发出光,光按照方向一直走并照亮走到的格子,直到碰到障碍为止,求最后有多少个格子被照亮。

\(1\le R,C\le 1500,1\le n\le 5\times 10^5,1\le m\le 10^5\)

solution

简单题,暴力遍历被照亮的格子即可,标记每个格子是否被横向或纵向的光经过,被同样方向的光经过则不需要再更新,时间复杂度 \(O(RC)\)

Code
#include<cstdio>
#include<algorithm>
#include<vector>
#include<bitset>
using namespace std;
#define ll long long
#define qwq Ff472130
#define f(i,l,r) for (int i=l;i<=r;i++)
#define F(i,l,r) for (int i=l;i>=r;i--)
constexpr int N=1500+10;
constexpr int inf=1e9+10;

inline void read(int &x) {
	x=0;
	char ch=getchar();
	while (ch<48) ch=getchar(); 
	while (ch>=48) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
}

int n,m,ans;
bitset<N> vis[N],vx[N],vy[N],did[N];

struct Node{int x,y;};
vector<Node> tmp;

inline void bfs() {
	for (Node k:tmp) {
		int x=k.x,y=k.y;
		F(i,x,1) {
			if (vis[i][y]||vx[i][y]) break;
			vx[i].set(y);
			ans+=(!did[i][y]);
			did[i].set(y);
		}
		f(i,x+1,n) {
			if (vis[i][y]||vx[i][y]) break;
			vx[i].set(y);
			ans+=(!did[i][y]);
			did[i].set(y);
		}
		F(i,y,1) {
			if (vis[x][i]||vy[x][i]) break;
			vy[x].set(i);
			ans+=(!did[x][i]);
			did[x].set(i);
		}
		f(i,y+1,m) {
			if (vis[x][i]||vy[x][i]) break;
			vy[x].set(i);
			ans+=(!did[x][i]);
			did[x].set(i);
		}
	}
}

int main() {
	read(n);read(m);
	int L1,L2;read(L1);read(L2);
	f(i,1,L1) {
		int x,y;read(x);read(y);
		tmp.push_back({x,y});
	}
	f(i,1,L2) {
		int x,y;read(x);read(y);
		vis[x].set(y);
	}
	bfs();
	printf("%d\n",ans);
	return 0;
}

T2: Fast XORting

题意

给定一个长度为 \(n\) 的排列,排列的值域是 \([0,n-1]\),保证 \(n\)\(2\) 的幂,你可以进行以下两种操作。

  • 交换序列相邻两项;
  • 选定 \(x(0\le x< n)\) 并将序列中所有数异或上 \(x\)

求将这个排列排序所需最小操作次数。

\(1\le n\le 2^{18}\)

solution

首先第二种操作最多使用一次,因为多次异或可以将所有异或上的数异或起来进行一次异或即可。

若不进行第二种操作,则答案为逆序对数,考虑进行第二种操作后,最少还需要多少次第一种操作。

从高到低考虑每一位,只考虑高于当前位的位都相同的一组数(因为更高位不同那么它们的大小关系已经在更高位确定,不需要在当前位计算),则逆序对数可以分成这一位为 \(0\) 与这一位为 \(1\) 之间的逆序对数与这一位相同的数之间的逆序对数。

全体异或上一个数可以将这一位反转,而不影响这些数的相对顺序,所以影响的只是这一位为 \(0\) 与这一位为 \(1\) 之间的逆序对数,那么考虑这一位是否被反转,两种情况产生的逆序对数取较小值即为这一位的答案,所有位的答案加起来即为总共需要操作一的次数。

时间复杂度 \(O(n\log n)\)

Code
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define qwq Ff472130
#define f(i,l,r) for (int i=l;i<=r;i++)
#define F(i,l,r) for (int i=l;i>=r;i--)
constexpr int N=1e6+10;
constexpr int inf=1e9+10;

inline void read(int &x) {
	x=0;
	char ch=getchar();
	while (ch<48) ch=getchar(); 
	while (ch>=48) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
}

int n,k;
int a[N],b[N],c[N];

struct BIT {
	int v[N];
	inline void clear(int n) {f(i,1,n)v[i]=0;}
	inline void add(int p) {for(;p<=n;p+=(p&-p))v[p]++;}
	inline int qry(int p) {int r=0;for(;p;p-=(p&-p))r+=v[p];return r;}
}tr;

inline ll calc() {
	ll res=0;
	tr.clear(n);
	F(i,n,1) {
		res+=tr.qry(a[i]);
		tr.add(a[i]+1);
	}
	return res;
}

inline ll work(int len) {
	int c1=0;ll res=0;
	f(i,1,len) {
		if (b[i]==0) res+=c1;
		else c1++;
	}
	return res;
}

inline ll solve(int d,int len) {
	f(i,1,n) c[i]=((a[i]>>d)&1);
	ll v1=0,v2=0;
	for (int i=1;i<=n;i+=len) {
		f(j,1,len) b[j]=c[j+i-1];
		v1+=work(len);
		f(j,1,len) b[j]=(c[j+i-1]^1);
		v2+=work(len);
	}
	if (v1>v2) f(i,1,n) a[i]^=(1<<d),c[i]^=1;
	for (int i=1;i<=n;i+=len) {
		vector<int> c0,c1;
		f(j,1,len) {
			if (c[i+j-1]) c1.push_back(a[i+j-1]);
			else c0.push_back(a[i+j-1]);
		}
		int now=i;
		for (int x:c0) a[now++]=x;
		for (int x:c1) a[now++]=x;
	}
	return min(v1,v2)+(d?solve(d-1,len/2):0);
}

int main() {
	read(n);
	f(i,1,n) read(a[i]);
	if (n==1) return puts("0"),0;
	ll ans=calc();
	f(i,0,18) if ((1<<i)==n) {k=i;break;}
	printf("%lld\n",min(ans,solve(k-1,n)+1));
	return 0;
}

T4: [ICPC 2023 Nanjing R] 红黑树

题单:

Culture Code

Bombs

posted @ 2026-07-21 17:29  Ff472130  阅读(1)  评论(0)    收藏  举报