初赛结束了!
要准备复赛了!
2019-10-21
xsy 3249
用
S
i
S_i
Si表示
2
i
∗
2
i
2^i*2^i
2i∗2i的矩阵
注意一下组成方案:
2
k
∗
2
k
2^k*2^k
2k∗2k的正方形,分成四个矩阵,‘J’,‘O’,‘I’,
S
k
−
1
S_{k-1}
Sk−1
现在有n个位置填了数
如果我们枚举
S
k
−
1
S_{k-1}
Sk−1填在当前矩阵的哪个位置
再枚举‘J’‘O’‘I’填哪里
那么剩下三个部分(‘J’‘O’‘I’)需要改变位置的个数就确定了!
这启示我们dp
建出一颗四叉树,每个节点都表示一个范围
[
l
1
,
r
1
]
[
l
2
,
r
2
]
[l1,r1][l2,r2]
[l1,r1][l2,r2]
里面的位置中,假设有
n
′
n'
n′个已经确定的
如果
n
′
=
0
n'=0
n′=0那么答案显然为0
这样,整个四叉树的节点个数就大大缩小了
具体来说,只有
n
∗
K
n*K
n∗K个
对于四叉树中每个节点的答案
如果
k
=
0
k=0
k=0,答案为0
其他,枚举
S
k
−
1
S_{k-1}
Sk−1填在哪里,剩下的’J’‘O’'I’填在哪里进行递推
复杂度
O
(
n
K
4
∗
3
!
)
O(nK4*3!)
O(nK4∗3!)
2019-10-23
[JOI 2012 春] Rotate
维护链表,每个点的上下左右
发现上下左右的点有变化的是
O
(
N
)
O(N)
O(N)级别的
但是翻转操作有点难维护
不放这样假设
每个点的上下左右都可以在当前维护的"上"“下”“左”“右”向右移动一些为
找到一个点真正的上下左右的话
找到(0,0)到(x,y)的一条路径
中间更新每个点的tag即可
这是因为(0,0)的tag不变
暴力更新部分,即更改上下左右差不多
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
#define Maxn 1100010
int ch[Maxn][4],tag[Maxn],num1[4][Maxn],num2[4][Maxn];
char s[Maxn],t[1010];
inline int calc(int x,int y){return x*(n+2)+(y+1);}
inline int get_nxt(int x,int y){
int to=ch[x][(y+tag[x])&3];
for(int i=0;i<4;++i)
if(ch[to][i]==x)tag[to]=(i-y+6)&3;
return to;
}
inline void solve(int x,int y,int k){
int node=1;
for(int i=1;i<=x;++i)node=get_nxt(node,0);
for(int i=1;i<=y;++i)node=get_nxt(node,1);
for(int i=0;i<4;++i)
for(int j=1;j<=k;++j){
num1[i][j]=node;
num2[i][j]=get_nxt(node,(i+3)&3);
if(j<k)node=get_nxt(node,i);
}
for(int i=0;i<4;++i)
for(int j=1;j<=k;++j){
ch[num1[i][j]][(tag[num1[i][j]]+i+3)&3]=num2[(i+1)&3][j];
ch[num2[i][j]][(tag[num2[i][j]]+i+1)&3]=num1[(i+3)&3][j];
}
}
int main(){
scanf("%d%d",&n,&m);
for(register int i=1;i<=n;++i){
scanf("%s",t+1);
for(register int j=1;j<=n;++j)s[calc(i,j)]=t[j];
}
for(register int i=0;i<=n+1;++i)
for(register int j=0;j<=n+1;++j){
if(i<=n)ch[calc(i,j)][0]=calc(i+1,j);
if(j<=n)ch[calc(i,j)][1]=calc(i,j+1);
if(i)ch[calc(i,j)][2]=calc(i-1,j);
if(j)ch[calc(i,j)][3]=calc(i,j-1);
}
int x,y,k;
while(m--){
scanf("%d%d%d",&x,&y,&k);
solve(x,y,k);
}
int node=calc(1,0);
for(register int i=1;i<=n;++i,node=get_nxt(node,0)){
int tmp=node;
for(register int j=1;j<=n;++j)putchar(s[tmp=get_nxt(tmp,1)]);
puts("");
}
return 0;
}
[JOI 2012 春] Constellation
2019-10-24
今天比赛
C题想的有点久
后面想A,如果一道题30 min中没有思路,那就换道题
最后是B,发现码量过大,此时应该背水一战,去刚A
考试前两个小时,最好是前90分钟,每道题都能稍微想一想
A题优秀题解
优秀
主要是非链底都是大于第i+1个的
2019-10-25
对于某种有先后顺序要求的题目,可以确定好先后顺序
然后就是经典的树的拓扑序个数个数了
2019-10-26
没吸取昨天的教训啊啊啊
[JOI 2015 春] Toilets
推了一波,把男的视为1,女的视为-1
那么任意一个后缀和不大于1即可
因为上厕所的过程相当于
每次取最前面的女的,再取最前面的
归纳证明
然后,这样答案相当于max(1,最大后缀和)-1
2019-10-28
我觉得我要开始技术统计了
2019-10-29
随便做的一场
然后我还挂题了…
多项式exp的
O
(
n
2
)
O(n^2)
O(n2)做法
不放设该多项式为
f
(
x
)
=
∑
i
a
i
x
i
i
!
f(x)=\sum_i a_i\frac{x^i}{i!}
f(x)=∑iaii!xi
于是我们可以这么理解exp的组合意义
选定
i
1
−
i
m
i_1-i_m
i1−im,令
S
=
∑
i
x
S=\sum i_x
S=∑ix
不放令
b
S
=
a
n
s
S
∗
i
!
b_S=ans_S*i!
bS=ansS∗i!
那么
b
S
=
∑
(
∏
a
i
x
∗
S
!
∏
(
i
x
)
!
/
m
!
)
b_S=\sum(\prod a_{i_x}*\frac{S!}{\prod (i_x)!}/m!)
bS=∑(∏aix∗∏(ix)!S!/m!)
相当于给这m个集合分配1~S的数,且每个集合中的最小数递增,于是可以愉快地dp了
2019-11-1
vector的功能可以用链表实现
最后的问题很简单,就是在最小生成树上,不在该树上的一条边会有一条路径,问覆盖一条边的最小值
从小到大加不在最小生成树上的边,类似缩点的那样,已经加入的边会形成连通块,不停更新这个,并查集最终的fa就是该联通块的根
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
#define Maxn 100010
#define E 1000010
int head[Maxn],v[Maxn<<1],w[Maxn<<1],nxt[Maxn<<1],tot=0;
int val[Maxn],res[Maxn][17],anc[Maxn][17],fa[Maxn];
struct Edge{
int s,e,t,id;
bool operator <(const Edge &z)const{return t<z.t;}
}edge[E];
inline void add_edge(int s,int e,int t){
tot++;v[tot]=e;w[tot]=t;nxt[tot]=head[s];head[s]=tot;
tot++;v[tot]=s;w[tot]=t;nxt[tot]=head[e];head[e]=tot;
}
int getroot(int x){
if(fa[x]!=x)fa[x]=getroot(fa[x]);
return fa[x];
}
int minv[Maxn],Ans[E],depth[Maxn];
bool choose[E];
void dfs(int u,int f){
anc[u][0]=f;
res[u][0]=val[u];
for(int i=1;i<=16;++i){
anc[u][i]=anc[anc[u][i-1]][i-1];
res[u][i]=max(res[u][i-1],res[anc[u][i-1]][i-1]);
}
for(int i=head[u];i;i=nxt[i])
if(v[i]^f){
val[v[i]]=w[i];
depth[v[i]]=depth[u]+1;
dfs(v[i],u);
}
}
inline int calc(int a,int b){
int ans=0;
if(depth[a]>depth[b]){
int d=depth[a]-depth[b];
for(int i=16;i>=0;--i)
if(d&(1<<i)){
ans=max(ans,res[a][i]);
a=anc[a][i];
}
}else{
int d=depth[b]-depth[a];
for(int i=16;i>=0;--i)
if(d&(1<<i)){
ans=max(ans,res[b][i]);
b=anc[b][i];
}
}
if(a==b)return ans;
for(int i=16;i>=0;--i)
if(anc[a][i]!=anc[b][i]){
ans=max(ans,res[a][i]);a=anc[a][i];
ans=max(ans,res[b][i]);b=anc[b][i];
}
ans=max(ans,val[a]);
ans=max(ans,val[b]);
return ans;
}
inline void rd(int &x){
x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
}
int main(){
rd(n);rd(m);
for(int i=1;i<=m;++i){
rd(edge[i].s);rd(edge[i].e);rd(edge[i].t);edge[i].id=i;
}
sort(edge+1,edge+m+1);
for(int i=1;i<=n;++i)fa[i]=i;
for(int i=1;i<=m;++i){
int fx=getroot(edge[i].s),fy=getroot(edge[i].e);
if(fx==fy)continue;
fa[fx]=fy;
add_edge(edge[i].s,edge[i].e,edge[i].t);
choose[i]=true;
}
dfs(1,0);
for(int i=1;i<=n;++i)fa[i]=i,minv[i]=1000000000;
for(int i=1;i<=m;++i)
if(!choose[i]){
Ans[edge[i].id]=calc(edge[i].s,edge[i].e);
int h1=getroot(edge[i].s),h2=getroot(edge[i].e);
for(;h1!=h2;h1=getroot(h1)){
if(depth[h1]<depth[h2])swap(h1,h2);
minv[h1]=edge[i].t;
fa[h1]=anc[h1][0];
}
}
for(int i=1;i<=m;++i)
if(choose[i]){
if(depth[edge[i].e]<depth[edge[i].s])swap(edge[i].s,edge[i].e);
Ans[edge[i].id]=minv[edge[i].e];
}
for(int i=1;i<=m;++i)printf("%d\n",Ans[i]);
return 0;
}