初三上学期比赛笔记part4

初赛结束了!
要准备复赛了!
2019-10-21
xsy 3249
S i S_i Si表示 2 i ∗ 2 i 2^i*2^i 2i2i的矩阵
注意一下组成方案: 2 k ∗ 2 k 2^k*2^k 2k2k的正方形,分成四个矩阵,‘J’,‘O’,‘I’, S k − 1 S_{k-1} Sk1
现在有n个位置填了数
如果我们枚举 S k − 1 S_{k-1} Sk1填在当前矩阵的哪个位置
再枚举‘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 nK
对于四叉树中每个节点的答案
如果 k = 0 k=0 k=0,答案为0
其他,枚举 S k − 1 S_{k-1} Sk1填在哪里,剩下的’J’‘O’'I’填在哪里进行递推
复杂度 O ( n K 4 ∗ 3 ! ) O(nK4*3!) O(nK43!)

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 i1im,令 S = ∑ i x S=\sum i_x S=ix
不放令 b S = a n s S ∗ i ! b_S=ans_S*i! bS=ansSi!
那么 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值