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

浙公网安备 33010602011771号