20210621模拟

Day1

T1

可以线段树维护区间线性基的并,然后查询

怎么查询呢?能贡献1就贡献1

但是我OJ上T了,lemon又RE了,打算把LCA写完再去调调(正解调完再说)

正解:

我们知道线性基是一个极大线性无关组,同时一个线性空间中的任意线性无关组是等价的

我们可以扫描线,枚举右端点,那么对于一个左端点,我们只需要知道这个区间内的一个线性无关组就可以了

我们考虑什么时候线性无关组会发生变化,假设我们从右往左加数的话,线性无关组变化意味着极大线性无关组大小的变化,因为新加的数一定和当前的极大线性无关组线性无关,于是极大线性无关组的大小会增加 1

同时我们知道线性基大小不会超过 30,于是我们可以对所有 i,在序列左边维护线性无关组大小为 i 时的一个线性基

考虑增加一个数时会产生怎样的变化,容易发现存在某个数 k,使得当 i ≤ k 时线性基大小 +1,当 i > k 时线性基大小不变。这样我们也可以得到从右往左大小第一次达到每个线性基时的端点。

考虑我们之前维护端点的过程,那么后 i 个端点实际上就是一个大小为 i 的线性无关组。

容易发现,我们将位置在前面的数异或位置在后面的数,并不会对我们的答案产生影响。所以实际上我们仍然可以维护线性基,并且只需维护 1 个

T2

连边跑最短路,但是更新的时候要加上等车的时间

T3

给 r 对无向边,求出一个最大的边集,使得不包含在同一对中的无向边,且不包括环

\(r^3\),但是T 1e4,行吧,这部分分设的真不错

Day2

T1

n 为偶数时显然奇数位的数位和等于偶数位的数位和,所以答案就是 \(10^{\frac{n}{2}}\)

n=4k+1 时,我们用\(x_i\)表示第 i 位的数字:

\(2(x_1 +x_3 +...+x_{\frac{n}{2}-1})+x_{\frac{n}{2}+1} =2(x_2 +x_4 +...+x_{\frac{n}{2}})\)

那么我们可以枚举 \(x_{\frac{n}{2}+1}\),问题变为求 \(\dfrac{n}{2}\) 个不超过 9 的非负整数和为 m 的方案数,这不难 O(n)容斥求出。

怎么容斥呢?我再想想,没好好上数学课就是不行啊……容斥都忘了,哎

我们可以先求出所有情况不管合不合法,然后减掉一个>10,加两个>10……

有i个大于10的情况数怎么求呢?首先我得选出哪i个盒子,C(n,i)

然后对于一种i个盒子,可以先给i个盒子每个各10个球,剩下的可以m-10\(\times\)i隔板

最后的答案就是\(C(n,i)\times C(m-10\times i+n-1,n-1)\)

n=4k+3 时与上面类似。

时间复杂度 O(tn)

T2

贪心的发现,如果奇偶分别的相对顺序不变,答案一定最小,这是充分条件,答案最小不一定相对顺序不变

我们可以求出他相对顺序不变时的情况,然后再调换成字典序最小

什么时候答案不变呢,假设两个元素的初始位置为\(a,b\),最后的第一次调换后的位置为\(pa,pb\),如果pa相对a和pb相对于b的方向相同,且调换后也满足这个性质就可以换

简单来说其实就是因为绝对值符号

之前是pa-a+pb-b,之后是pa-b+pb-a,不难发现二者是相等的,这样每次遇到一个可调换的位置,把待调换的最小值放上去就好了

我是用优先队列维护的,代码出奇的长,看网上写的才40行,我写了120行左右,其实有很多重复的部分,我合并了其中的一小部分,而网上的代码用一些打标记的位运算合并了所有情况

其实我写的时候还是蛮冷静了,思路也算清晰,看一下把看一下吧

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define B cout<<"Breakpoint"<<endl;
#define O(x) cout<<#x<<" "<<x<<endl;
#define o(x) cout<<#x<<" "<<x<<" ";
using namespace std;
int read(){
	int x = 1,a = 0;char ch = getchar();
	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
	return x*a;
}
const int maxn = 1e5+10;
int n,a[maxn];
struct node{
	int pos,op;
}spot[maxn];
bool cmp1(node x,node y){return (x.pos == y.pos) ? x.op < y.op : x.pos < y.pos;}
bool cmp2(node x,node y){return (x.pos == y.pos) ? x.op > y.op : x.pos < y.pos;}
int ans1[maxn],ans2[maxn],pos1[maxn],pos2[maxn];
void getans(int op,int l,int p[],int answer[]){
	priority_queue<int,vector<int>,greater<int> > q1;
	priority_queue<int> q2;
	int cnt = 0;
	for (int i = 1;i <= n;i++){
		if (p[i] >= i&&a[i]%2 == op) spot[++cnt] = (node){i,0},spot[++cnt] = (node){p[i],1};
	}
	sort(spot+1,spot+cnt+1,cmp1);
	for (int i = 1;i <= cnt;i++){
		if (spot[i].op == 0) q1.push(a[spot[i].pos]);
		else{
			int x = q1.top();q1.pop();
			answer[spot[i].pos] = x;
		}
	}
	cnt = 0;
	for (int i = 1;i <= n;i++){
		if (p[i] < i&&a[i]%2 == op) spot[++cnt] = (node){i,0},spot[++cnt] = (node){p[i],1};
	}
	sort(spot+1,spot+cnt+1,cmp2);
	for (int i = cnt;i >= 1;i--){
		if (spot[i].op == 0) q2.push(a[spot[i].pos]);
		else{
			int x = q2.top();q2.pop();
			answer[spot[i].pos] = x;
		}
	}
}
void subtask1(){
	int cnt1 = 0,cnt2 = -1;
	for (int i = 1;i <= n;i++){
		if (a[i]&1) pos1[i] = (cnt1 += 2);
		else pos2[i] = (cnt2 += 2);
	}
	getans(0,cnt2,pos2,ans2),getans(1,cnt1,pos1,ans1);
	for (int i = 1;i <= n;i++){
		if (i&1) printf("%d ",ans2[i]);
		else printf("%d ",ans1[i]);
	} 
}
int anss1[maxn],anss2[maxn];
void subtask2(){
	int cnt1 = 0,cnt2 = -1;
	int num1 = 0,num2 = 0;
	for (int i = 1;i <= n;i++){
		if (a[i]&1) pos1[i] = (cnt1 += 2),num1 += abs(i-pos1[i]);
		else pos2[i] = (cnt2 += 2),num1 += abs(i-pos2[i]);
	}
	getans(0,cnt2,pos2,ans2),getans(1,cnt1,pos1,ans1);
	for (int i = 1;i <= n;i++){
		if (i&1) anss1[i] = ans2[i];
		else anss1[i] = ans1[i];
	} 
	cnt1 = -1,cnt2 = 0;
	for (int i = 1;i <= n;i++) pos1[i] = pos2[i] = ans1[i] = ans2[i] = 0;
	for (int i = 1;i <= n;i++){
		if (a[i]&1) pos1[i] = (cnt1 += 2),num2 += abs(i-pos1[i]);
		else pos2[i] = (cnt2 += 2),num2 += abs(i-pos2[i]);
	}
	getans(0,cnt2,pos2,ans2),getans(1,cnt1,pos1,ans1);
	for (int i = 1;i <= n;i++){
		if (i&1) anss2[i] = ans1[i];
		else anss2[i] = ans2[i];
	} 
	if (num1 < num2) for (int i = 1;i <= n;i++) printf("%d ",anss1[i]);
	if (num1 > num2) for (int i = 1;i <= n;i++) printf("%d ",anss2[i]);
	if (num1 == num2){
		if (anss1[1] < anss2[1]) for (int i = 1;i <= n;i++) printf("%d ",anss1[i]);
		else for (int i = 1;i <= n;i++) printf("%d ",anss2[i]);
	}
}
void subtask3(){
	int cnt1 = -1,cnt2 = 0;
	for (int i = 1;i <= n;i++){
		if (a[i]&1) pos1[i] = (cnt1 += 2);
		else pos2[i] = (cnt2 += 2);
	}
/*	for (int i = 1;i <= n;i++) cout<<pos1[i]<<" ";
	cout<<endl;
	for (int i = 1;i <= n;i++) cout<<pos2[i]<<" ";
	cout<<endl;
*/
	getans(0,cnt2,pos2,ans2),getans(1,cnt1,pos1,ans1);
	for (int i = 1;i <= n;i++){
		if (i&1) printf("%d ",ans1[i]);
		else printf("%d ",ans2[i]);
	} 
}
int main(){
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	n = read();int num = 0;
	for (int i = 1;i <= n;i++) a[i] = read(),num += (a[i]&1);
	if (num < n-num) subtask1();
	if (num == n-num) subtask2();
	if (num > n-num) subtask3();
	return 0;
}
/*
5
5 3 1 4 2
*/

Day3

T1

构造题,就是看一共能构造出多少个不同方向上的'L'形

发现对于一个\(2\times 2\)的一个方格对答案的贡献为4,如果方格缺一个角对答案的贡献为1

这样我们每次尽可能多的往答案上凑

\(k\geq 2\times(m-1)\),说明我一整行都可以填满,那我就填一整行

否则设一个量\(tmp = \frac{(k-1)}{4}\)为什么减一呢,因为我这一行一定填不满,一定会出现四个\(2\times 2\)的方格缺一个角,最后会多出一个贡献

这样我最开始连续填入\(\frac{(k-1)}{4}+1\)个'',这是整方格的贡献,剩下的我交叉着填,除了最后一个格子,每填一个'',左右都会产生一个残缺的方格,他会产生2的贡献,最后一个格子会产生1的贡献

如果还是没填完,剩余的k只有1,2,3,4四种取值,我们分情况讨论

如果k = 1,行首填一个'*'就好了,当然有其他方案

如果k = 2,上一行头会有两种情况''或者'.',第一种情况,当前行可以填'.',第二种情况,可以在第一行挖一个角,然后当前行填'***'

如果k = 3,包括他剩下的情况,上一行都是填满的状态,可以自己构造一种简单的方案

T2

看标程感觉是写不出来的题,40分nq,对于每个询问,枚举n个点如果他们对一个查询点距离<=k,贡献就加1,

但是好像数据很水,gjz nk 50000$\times$500000过了70!

T3

肉眼识别了近1000组,然后就放弃了,后来才反应过来,可以跟train进行比较,然后输出嘴相似的那一幅图片

posted @ 2021-06-21 15:58  小又又yyyy  阅读(38)  评论(0编辑  收藏  举报