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( imes)i隔板
最后的答案就是(C(n,i) imes C(m-10 imes 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 imes 2)的一个方格对答案的贡献为4,如果方格缺一个角对答案的贡献为1
这样我们每次尽可能多的往答案上凑
当(kgeq 2 imes(m-1)),说明我一整行都可以填满,那我就填一整行
否则设一个量(tmp = frac{(k-1)}{4})为什么减一呢,因为我这一行一定填不满,一定会出现四个(2 imes 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$ imes$500000过了70!
T3
肉眼识别了近1000组,然后就放弃了,后来才反应过来,可以跟train进行比较,然后输出嘴相似的那一幅图片