zr2019暑期高端峰会AB组day3

本文深入探讨了算法竞赛中常见的几种题目类型,包括游戏策略、画画路径与交易模拟,分享了高效解题思路与代码实现,如维护环的链表、两端延伸的队列及LCT算法在交易模拟中的应用。

zr2019暑期高端峰会AB组day3

A. 游戏

link

这题考场上花了不少时间,虽然想的是小题大做维护环的链表但总归正确性是有的,但是有傻逼错误, O ( 1 ) O(1) O(1)的函数我用 O ( n ) O(n) O(n)实现直接起飞,我拍了几百组1000的数据本以为稳了,下次一定要记得造极限数据测下时间

  • 这题做法八仙过海,其中有一波是注意到了分别维护奇数位置的数和偶数位置的数,每次就是交换这两个数列或者做小小的变动,于是搞个两端延申的队列就行了,这个好妙,没想到
  • 我的做法属于另一波没注意到这个性质的做法,假如一个木棍都不删除,我们可以 O ( 1 ) O(1) O(1)求出第a层第b列的小球编号是多少,那么我们考虑那些被抽掉的木棍,相当于我们交换了应该出现在这两个位置的小球的最终答案,swap就行了,结果我还写了个链表维护断环并环

B. 画画

link

这题的两个小结论

  • 不管怎么走,最后走出来的形状是一样的,这里的形状和题目定义已知
  • 那既然这样,那么理论上方案唯一,但是由于这两条路径会相交,相交的话我们就可以有两种选择,所以假设有 c n t cnt cnt个交点,那么答案就是 2 c n t 2^{cnt} 2cnt

于是我们安排两个人,同时从左上角走到右下角,一个能往右就往右,另一个能往下就往下,如果相交就 c n t + + cnt++ cnt++

于是它就是一个很妙的脑筋急转弯结论题

C. 交易

link

部分分什么的蛇皮做法就不细说了,点分治的正解好像没太搞懂,但是LCT的做法是真的妙

  • 我们关注一条边,当它关上的那一刻之前,两边的联通块的信息必然相容,这很显然,当它再次打开的时候两边的联通块信息在再次互补,但是上次已经交流过的信息不会产生贡献,那我们设两个联通块这是的信息量分别是 v a l 1 , v a l 2 val_1,val_2 val1,val2,这条边上次关闭前它们的共同信息量是 l a s t last last,那么当这条边再次打开的时候,这整个联通块的信息量会变成 v a l 1 + v a l 2 − l a s t val_1+val_2-last val1+val2last
  • 所以我们并不用维护每个点有的信息具体是些什么,只要维护每个联通块掌握的信息量即可
  • 那么我们将这个信息量维护在原树上深度最低的那个点上,然后就可以了

还要感谢pb大佬对我这个菜鸡的帮扶

T1代码
#include<bits/stdc++.h>
#define FOR(i,a,b) for (register int i=(a);i<=(b);i++)
#define For(i,a,b) for (register int i=(a);i>=(b);i--)
#define mem(i,j) memset(i,j,sizeof(i))
using namespace std;
typedef long long ll;
const int N=2e5+5;
int h,w,n,a[N],b[N],x[N],y[N],tmp[N],ans[N],back[N],mk[N],out[N];
int sb,vis[N];
int v[2005][2005],pos[N];
struct modify
{
	int a,b;
}q[N];
bool cmp(const modify x,const modify y) {return x.a<y.a;}
int read()
{
	int x=0,f=1;
	char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return f*x;
}
int Get_num(int col,int round,int pos)
{
	int ret=pos;
	int dir;
	if ((pos%2==0&&round%2==1)||(pos%2==1&&round%2==0)) dir=-1;
	else dir=1;
	round%=col*2;
	while (round>0)
	{
		int del,move;
		if (dir==1) del=col-ret;
		else del=ret-1;
		if (dir==1)
		{
			move=min(round,del);
			ret+=move;
			round-=move;
			round--;
			dir*=-1;
		}
		else 
		{
			move=min(round,del);
			ret-=move;
			round-=move;
			round--;
			dir*=-1;
		}
	}
	return ret;
}
void Debug()
{
	while (1)
	{
		int x=read(),y=read(),z=read();
		printf("%d\n",Get_num(x,y,z));
	}
	return;
}
void Solve()
{
	FOR(i,1,w) ans[i]=i;
	FOR(i,1,n) v[a[i]][b[i]]=1;
	FOR(i,1,h)
	{
		if (i%2==1)
			for (int j=1;j<w;j+=2) if (!v[i][j]) swap(ans[j],ans[j+1]);
		if (i%2==0)
			for (int j=2;j<w;j+=2) if (!v[i][j]) swap(ans[j],ans[j+1]);
	}
	FOR(i,1,w) pos[ans[i]]=i;
	FOR(i,1,w) printf("%d\n",pos[i]);
	return;
}
int main()
{
//	freopen("data.in","r",stdin);
//	freopen("myans.out","w",stdout);
	h=read(),w=read(),n=read();
	FOR(i,1,n) a[i]=read(),b[i]=read(),q[i]=(modify){a[i],b[i]};
	sort(q+1,q+n+1,cmp);
	FOR(i,1,n) x[i]=Get_num(w,q[i].a,q[i].b),y[i]=Get_num(w,q[i].a,q[i].b+1);
	FOR(i,1,w) tmp[i]=Get_num(w,h,i);
	if (n<=2000)
	{
		Solve();
		exit(0);
	}
	FOR(i,1,n)
	{
		if (x[i]==y[i]) continue;
		if ((!back[x[i]])&&(!back[y[i]]))
		{
			mk[x[i]]=y[i];
			mk[y[i]]=x[i];
			back[x[i]]=y[i];
			back[y[i]]=x[i];
		}
		else if (back[x[i]]&&(!back[y[i]]))
		{
			mk[y[i]]=x[i];
			mk[back[x[i]]]=y[i];
			back[y[i]]=back[x[i]];
			back[x[i]]=y[i];
		}
		else if (back[y[i]]&&(!back[x[i]]))
		{
			mk[x[i]]=y[i];
			mk[back[y[i]]]=x[i];
			back[x[i]]=back[y[i]];
			back[y[i]]=x[i];
		}
		else if (back[x[i]]&&back[y[i]])//problem here
		{
			int p1=x[i],p2=y[i];
			if (mk[p1]==p2&&mk[p2]==p1)
			{
				mk[p1]=mk[p2]=0;
				back[p1]=back[p2]=0;
			}
			else if (mk[p1]==p2)
			{
				mk[back[p1]]=p2;
				back[p2]=back[p1];
				mk[p1]=back[p1]=0;
			}
			else if (mk[p2]==p1)
			{
				mk[back[p2]]=p1;
				back[p1]=back[p2];
				mk[p2]=back[p2]=0;
			}
			else
			{
				int t1=back[p2],t2=back[p1];
				mk[t1]=p1;
				back[p1]=t1;
				mk[t2]=p2;
				back[p2]=t2;
			}
		}
	}
	FOR(i,1,w)
		if (back[tmp[i]]) ans[i]=back[tmp[i]];
		else ans[i]=tmp[i];
	FOR(i,1,w) out[ans[i]]=i;
	FOR(i,1,w) vis[out[i]]=1;
	FOR(i,1,w) if (!vis[i]) sb=i;
	FOR(i,1,w) if (!out[i]) out[i]=sb;
	FOR(i,1,w) printf("%d\n",out[i]);
//	Debug();
	return 0;
}
T2赛后代码
#include<bits/stdc++.h>
#define FOR(i,a,b) for (register int i=(a);i<=(b);i++)
using namespace std;
const int N=1e6+5;
const int mod=1e9+7;
int T,n,a[N],b[N],a1,a2,b1,b2,tag,cnt,same;
int read()
{
	int x=0,f=1;
	char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return f*x;
}
int qpow(int x,int y)
{
	int ret=1;
	while (y)
	{
		if (y&1) ret=1LL*ret*x%mod;
		y>>=1;
		x=1LL*x*x%mod;
	}
	return ret;
}
int main()
{
	T=read();
	while (T--)
	{
		tag=1,cnt=0,same=1;
		n=read();
		FOR(i,1,n) a[i]=read();
		FOR(i,1,n) b[i]=read();
		a1=b1=a2=b2=1;
		a[1]--;
		b[1]--;
		FOR(i,1,n*2-2)
		{
			if (a[a1]&&b1<n) b1++;
			else a1++;
			if (b[b2]&&a2<n) a2++;
			else b2++;
			a[a1]--,b[b2]--;
			if (a1!=a2||b1!=b2) a[a2]--,b[b1]--,same=0;
			else if (!same) cnt++,same=1;
		}
		FOR(i,1,n) if (a[i]||b[i]) tag=0;
		if (!tag) printf("0\n");
		else printf("%d\n",qpow(2,cnt));
	}
	return 0;
}
T3赛后代码
#include<bits/stdc++.h>
#define FOR(i,a,b) for (register int i=(a);i<=(b);i++)
using namespace std;
const int N=2e5+5;
int n,m,q,u[N],v[N];
int pre[N],ch[N][2],rev[N];
int query[N],ans[N],val[N],opt[N],now[N],last[N];
//把联通块的信息维护在了原树深度最浅的点的val上 
//所以所有破坏树结构的操作(带makeroot) 都要重新调整val的位置 
//当然pb大佬教我的不破坏树结构的方法踩了我一百毫秒 
int read()
{
	int x=0,f=1;
	char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return f*x;
}

int nroot(int x) {return ch[pre[x]][0]==x||ch[pre[x]][1]==x;}
void Update_rev(int p) {if (!p) return;swap(ch[p][0],ch[p][1]);rev[p]^=1;return;}
void Push(int p) {if (!rev[p]) return;if (!rev[p]) return;Update_rev(ch[p][0]);Update_rev(ch[p][1]);rev[p]=0;return;}
void Rotate(int x)
{
	int y=pre[x],k=(ch[y][0]==x);
	ch[y][!k]=ch[x][k];
	if (ch[x][k]) pre[ch[x][k]]=y;
	if (pre[y]&&nroot(y)) ch[pre[y]][(ch[pre[y]][1]==y)]=x;
	pre[x]=pre[y];
	pre[y]=x;
	ch[x][k]=y;
	return;
}
void Pushall(int x)
{
	if (nroot(x)) Pushall(pre[x]);
	Push(x);
	return;
}
void Splay(int x)
{
	Pushall(x);
	while (nroot(x))
	{
		int y=pre[x],z=pre[y];
		if (nroot(y))
		{
			if ((ch[z][1]==y)xor(ch[y][1]==x)) Rotate(x);
			else Rotate(y);
		}
		Rotate(x);
	}
	return;
}
void Access(int x)
{
	int y=0;
	for (;x;x=pre[y=x])
	{
		Splay(x);
		ch[x][1]=y;
	}
	return;
}
int Getrt(int x)
{
	Access(x);
	Splay(x);
	while (ch[x][0]) x=ch[x][0];
	Splay(x);
	return x;
}
void Make_root(int x)
{
	int tmpval=val[Getrt(x)];
	Access(x);
	Splay(x);
	Update_rev(x);
	val[x]=tmpval;
	return;
}
void Link(int u,int v)
{
	Make_root(u);
	pre[u]=v;
	return;
}
void Cut(int u,int v)
{
	int tmpval=val[Getrt(u)];
	Make_root(u);
	Access(v);
	Splay(v);
	pre[u]=ch[v][0]=0;
	val[Getrt(u)]=val[Getrt(v)]=tmpval;
	return;
}
int main()
{
	n=read(),m=read(),q=read();
	FOR(i,1,n-1) u[i]=read(),v[i]=read();
	FOR(i,1,m) opt[i]=read();
	FOR(i,1,q) query[i]=read();
	FOR(i,1,n) val[i]=1;
	FOR(i,1,m)
	{
		if (!now[opt[i]])
		{
			int v1=val[Getrt(u[opt[i]])],v2=val[Getrt(v[opt[i]])];
			Link(u[opt[i]],v[opt[i]]);
			val[Getrt(u[opt[i]])]=v1+v2-last[opt[i]];
		}
		else
		{
			last[opt[i]]=val[Getrt(u[opt[i]])];
			Cut(u[opt[i]],v[opt[i]]);
		}
		now[opt[i]]^=1;
	}
	FOR(i,1,q) ans[i]=val[Getrt(query[i])];
	FOR(i,1,q) printf("%d\n",ans[i]);
	return 0;
}
代码下载地址: https://pan.quark.cn/s/35e46f7e83fb 关于 Build Status Lines of code 这是一个参考 PotPlayer 的界面使用 Java 以及图形界面框架 JavaFX 使用 MCV 图形界面与业务逻辑分离的开发模式, 所开发的个人视频播放器项目, 开发这个项目旨在于学习图形界面框架 JavaFX 实现了具有和 PotPlayer相同 的简洁界面和流畅的操作逻辑。 Note: PotPlayer 是 KMPlayer 的原制作者姜龙喜先生(韩国)进入 Daum 公司后的 新一代网络播放器, PotPlayer的优势在于强大的内置解码器以及支持各类的 视频格式, 而且是免费下载提供使用的。 目前版本: 2020/10/28 v1.0.0 [x] 支持打开文件自动播放 [x] 支持查看播放记录 [x] 支持屏幕边沿窗口自动吸附 [x] 支持双击视频来播放和暂停 [x] 支持左键点击窗口任意位置来拖到窗口 [x] 支持左键双击播放窗口打开文件 [x] 支持根据视频尺寸自动调整窗口大小 [x] 支持根据播放文件类型调整窗口模式 [x] 支持根据视频尺寸自动调整窗口显示位置防止超出屏幕 [x] 支持记录上一次访问的文件路径 [x] 支持播放记录文件读写 已实现样式 未播放效果: 播放效果: 运行环境 本项目使用 NetBeans 配合 JDK 开发, NetBeans8.0 以及 JDK8.0 以上版本的均可以运行。 亦可使用其他集成开发环境, 例如 Eclipse, IntelliJ IDEA 配合使用 JDK8.0 以上版本均可构建此项目。 NetBeans download Eclipse downlo...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值