E. MEX and Increments
F. Let’s Play the Hat?
G. Unusual Minesweeper
H. Permutation and Queries
用个优先队列模拟。
map<int,int>ma;
priority_queue<int> q;
int main()
{
int t;
scanf("%d", &t);
while(t --)
{
int n;
scanf("%d", &n);
while(q.size())q.pop(); ma.clear();
q.push(-1); // 初始不用特判,好像也没啥用
for(int i = 1;i <= n;i ++)
{
int x;
scanf("%d", &x);
ma[x] ++;
}
LL ans = 0;
for(int i = 0;i <= n;i ++)
{
if(!q.size() || ans == -1)
{
ans = -1, cout<<ans<<' ';
continue;
}
else ans += i-1-q.top(), cout<<ans+ma[i]<<' ', q.pop(); // ans +的是 成为i-1,再加i的个数
while(ma[i]) q.push(i), ma[i]--; // 加入队列。
}
puts("");
}
return 0;
}
直接模拟
int now = 0, n;
void dp(int a, int b) // 产生 a 个 b人 的 桌子
{
for(int i = 1;i <= a;puts(""), i ++)
{
printf("%d", b);
for(int i = 1;i <= b;now = (now+1)%n, i ++)
printf(" %d", now+1);
}
return ;
}
int main()
{
int t;
scanf("%d", &t);
while(t --)
{
int m, k;
scanf("%d%d%d", &n, &m, &k);
int x = m-n%m, y = n/m; // x 是下取整的桌子树
int a = n%m, b = (n+m-1)/m; // a 是上取整的桌子数
now = 0;
while(k --)
{
int t;
dp(a, b); t = now;
dp(x, y); now = t; // 下一次游戏接着 上一次游戏的最后一个的下一个摆
}
}
return 0;
}
并查集合并 + 贪心,写的有点复杂
const int N = 3e5 + 10, mod = 998244353;
struct P{
int x, y, id;
bool operator < (const P& b)const{return x == b.x ? y < b.y : x < b.x;}
};
P a[N];
int fa[N], time[N];
int find(int u){return fa[u] == u ? u : fa[u] = find(fa[u]);}
void merge(int u, int v){fa[find(u)] = find(v);}
int main()
{
int t;
scanf("%d", &t);
for(int _ = 1;_ <= t;_ ++)
{
int n, k;
scanf("%d%d", &n, &k);
for(int i = 1;i <= n;i ++)
{
scanf("%d%d%d", &a[i].x, &a[i].y, time+i);
a[i].id = i;
fa[i] = i;
}
sort(a+1, a+n+1);
for(int i = 1;i < n;i ++) // 按照x合并
{
if(a[i].x == a[i+1].x && a[i+1].y <= a[i].y+k)
merge(a[i].id, a[i+1].id);
swap(a[i].x, a[i].y);
}
swap(a[n].x, a[n].y); // 这步在上面最后一次没到 n 需要加上。
sort(a+1, a+n+1);
for(int i = 1;i < n;i ++) // 按照y合并
if(a[i].x == a[i+1].x && a[i+1].y <= a[i].y+k)
merge(a[i].id, a[i+1].id);
for(int i = 1, t;i <= n;i ++)
if((t = find(i)) != i)
time[t] = min(time[t], time[i]), time[i] = -1; // = -1代表不需要考虑
sort(time+1, time+n+1);
int ans = -1, pos = n;
while(pos)
if(time[pos--] > ans) ans++;
else break;
printf("%d\n", ans);
}
return 0;
}
原来分块是这样写的啊,在链表上分块,jump[i]代表i进行block次next得到的位置,然后查询就是直接查询,修改那就是先把x和y改了,然后还需要改一些数字的jump[],这些数字就是x的前block个pre,y的前block的pre,好牛啊,卧槽。
int r[N], l[N], jump[N], block;
void change(int x)
{
int to = x, i = block;
while(i) to = r[to], i--;
i = block;
while(i) jump[x] = to, x = l[x], to = l[to], i --;
}
int main()
{
int n, q;
scanf("%d%d", &n, &q);
for(int i = 1;i <= n;i ++) scanf("%d", r+i), l[r[i]] = i;
block = sqrt(n) + 1;
for(int i = 1;i <= n;i ++)
{
jump[i] = i;
for(int j = 1;j <= block;j ++)
jump[i] = r[jump[i]];
}
while(q --)
{
int p, x, y;
scanf("%d%d%d", &p, &x, &y);
if(p == 1){
swap(r[x], r[y]);
l[r[x]] = x, l[r[y]] = y; // 先改 x, y
change(x), change(y); // 再改 jump[]
}
else{
while(y >= block) x = jump[x], y -= block;
while(y) x = r[x], y --;
printf("%d\n", x);
}
}
return 0;
}