【训练小结】Asia Jakarta 2018

30 篇文章 0 订阅
10 篇文章 0 订阅
contest题解
B

题意
一棵树,每个节点一个齿轮,相邻节点联动(方向相反)。有三种操作

  1. 删除一个节点上的齿轮,使得周围的齿轮不连通
  2. 复原一个齿轮
  3. 旋转一个齿轮
    每次旋转操作输出总旋转角度,并且最后求每个齿轮的角度和

一道数据结构好题!
首先对于方向,可以看成抑或,所以到根差分一下,深度为奇数的点所有操作和最后结果取负
删除和加入对于度数很大的节点难以处理,因为是一棵树,想到和fa联系起来
找到和当前点x相连的最浅的祖先y。直接对y的子树进行修改。所有的修改把贡献进行差分。
具体来说,
一个点的角度=它的修改量-最近的被拿走的祖先的多余修改量
最后一次性加入所有被删除的点,使得多余修改量被加回来。
一个点和其子树的联通块大小=sz[x] - 子树中所有被删除的联通块之和
注意每次加入和删除的时候都要和y差分,保证维护的答案符合性质
怎么求y?
链剖,每个链维护一个set,二分即可

这道题整整写了3个小时:30min想细节,60min写代码,1h20min调题,非常糟糕!
总结所有的错误:
差分没有完全想清楚,一开始删除点的lastval求错
找y的时候在链上的标号和点标号混了,然后如果是轻儿子往上跳的特判写错了
打错了线段树和其他一个字符
一开始觉得代码复杂有些不敢写,写完发现其实逻辑非常清楚,但是细节注意的不够好。必须做到零错误,才能成为合格的数据结构选手!

#include<bits/stdc++.h>
using namespace std;

#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) (x&(-x))

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;

const ld inf = 2e18;
const int N = 3e6 + 10;
const int maxn = 100020;
const ll mod = 1e9 + 7;

inline ll power(ll x,ll y){
	y = ((y % (mod - 1)) + (mod - 1)) % (mod - 1);
//	if ( y < 0 ) return power(power(x,-y),mod - 2);
	ll res = 1;
	while ( y ){
		if ( y & 1 ) res = res * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return res;
}

struct node{
	int next,to;
}e[maxn * 2];
int head[maxn],cnt;
int lastval[maxn],tag[maxn];
int n;

//=======================lian pou======================================================
int fa[maxn],son[maxn],top[maxn],sz[maxn],dth[maxn],dfstime,id[maxn],l[maxn],r[maxn];
set <pr> v[maxn];

void dfs(int x){
	sz[x] = 1;
	fore(i,x){
		if ( e[i].to == fa[x] ) continue;
		fa[e[i].to] = x , dth[e[i].to] = dth[x] + 1;
		dfs(e[i].to);
		sz[x] += sz[e[i].to];
		if ( sz[son[x]] < sz[e[i].to] ) son[x] = e[i].to;
	}
}
void dfs2(int x){
	l[x] = ++dfstime;
	if ( son[x] ){
		top[son[x]] = top[x] , id[son[x]] = id[x] + 1;
		dfs2(son[x]);
	}
	fore(i,x){
		if ( son[x] == e[i].to || fa[x] == e[i].to ) continue;
		top[e[i].to] = e[i].to , id[e[i].to] = 1;
		dfs2(e[i].to);
	}
	r[x] = dfstime;
}
inline void adde(int x,int y){
	e[++cnt].to = y;
	e[cnt].next = head[x];
	head[x] = cnt;
}

void init(){
	dth[1] = 2 , id[1] = 1;
	dfs(1) , top[1] = 1, dfs2(1);
}
//========================================================================

namespace Seg{
#define ls(x) (x << 1)
#define rs(x) ((x << 1) | 1)
	const int M = 1e5 + 20;
	int add[M << 2],val[M << 2],s[M << 2];
	
	inline void Add(int x,int d){
		val[x] += d , add[x] += d;
	}
	inline void pushdown(int x){
		if ( add[x] ){
			Add(ls(x),add[x]);
			Add(rs(x),add[x]);
			add[x] = 0;
		}
	}
	void modify(int x,int l,int r,int L,int R,int d){
	//	if ( x == 1 ) cout<<L<<" "<<R<<" "<<d<<endl;
		if ( L <= l && R >= r ) { Add(x,d); return; }
		int mid = (l + r) >> 1;
		pushdown(x);
		if ( L <= mid ) modify(ls(x),l,mid,L,R,d);
		if ( R > mid ) modify(rs(x),mid + 1,r,L,R,d);
		//update(x);
	}
	inline void update(int x){
		s[x] = s[ls(x)] + s[rs(x)];
	}
	void modify(int x,int l,int r,int id,int d){
		if ( l == r ){
			s[x] += d;
			return;
		}
		int mid = (l + r) >> 1;
		if ( id <= mid ) modify(ls(x),l,mid,id,d);
		else modify(rs(x),mid + 1,r,id,d);
		update(x);
	}
	int query(int x,int l,int r,int L,int R){
		if ( L > R ) return 0;
		if ( L <= l && R >= r ) return s[x];
		int mid = (l + r) >> 1; int res = 0;
		if ( L <= mid ) res = query(ls(x),l,mid,L,R);
		if ( R > mid ) res += query(rs(x),mid + 1,r,L,R);
		return res;
	}
	int query(int x,int l,int r,int id){
		if ( l == r ) return val[x];
		int mid = (l + r) >> 1;
		pushdown(x);
		if ( id <= mid ) return query(ls(x),l,mid,id);
		return query(rs(x),mid + 1,r,id);
	}	
}
using namespace Seg;

inline int findNotTake(int x){
	int res = 0;
	while ( x ){
		int y = top[x];
		auto it = v[y].upper_bound(mp(id[x],x));
		if ( it == v[y].begin() ){
			res = top[x] , x = fa[top[x]];
			continue;
		}
		y = (*(--it)).se;
		if ( fa[res] == y ) return res;
		return son[y];
	}
	return 1;
}
void rotate(int x,int a){
	int p = findNotTake(x),tp = (dth[x] & 1) ? -1 : 1;
//	cout<<x<<" "<<p<<" "<<a<<endl;;
	modify(1,1,n,l[p],r[p],a * tp);
	printf("%d\n",a * (sz[p] - query(1,1,n,l[p],r[p])));
}
void takeOut(int x){
	int p = findNotTake(x),q = fa[p];
	int curs = sz[x] - query(1,1,n,l[x],r[x]);
	modify(1,1,n,l[x],curs);
	lastval[x] = query(1,1,n,l[x]);
	if ( q ){
		modify(1,1,n,l[q],-curs);
		lastval[x] += lastval[q] - query(1,1,n,l[q]);
	}

	v[top[x]].insert(mp(id[x],x));
	tag[x] = 1;
}
void putIn(int x){
	v[top[x]].erase(mp(id[x],x));
	int p = findNotTake(x);
	int q = fa[p],curs = query(1,1,n,l[x],l[x]);
	int curv = query(1,1,n,l[x]);
	
	tag[x] = 0;
	if ( !q ){
		modify(1,1,n,l[x],-curs);
		modify(1,1,n,l[x],r[x],lastval[x] - curv);
		return;	
	}

	modify(1,1,n,l[x],-curs);
	modify(1,1,n,l[q],curs);
	int vq = lastval[q] - query(1,1,n,l[q]);
	modify(1,1,n,l[x],r[x],lastval[x] - curv - vq); //x 的val = 第一个被删除的点的delta + 真实值
}
int main(){
//	freopen("input.txt","r",stdin);
	scanf("%d",&n);
	rep(i,1,n - 1){
		int x,y;
		scanf("%d %d",&x,&y);
		adde(x,y) , adde(y,x);
	}
	init();
	int q;
	scanf("%d",&q);
//	rep(i,1,n) cout<<l[i]<<" ";
//	cout<<endl;
	while ( q-- ){
		int tp , x , a;
		scanf("%d",&tp);
		if ( tp == 1 ) {
			scanf("%d",&x);
			takeOut(x);
		}
		else if ( tp == 2 ){
			scanf("%d",&x);
			putIn(x);
		}
		else{
			scanf("%d %d",&x,&a);
			rotate(x,a);
		}
	}
	rep(i,1,n){
		if ( tag[i] ) putIn(i);
	}
	int sum = 0;
	rep(i,1,n){
	//	cout<<(query(1,1,n,l[i]) * ((dth[i] & 1) ? -1 : 1) % 360 + 360) % 360<<endl;
		sum += (query(1,1,n,l[i]) * ((dth[i] & 1) ? -1 : 1) % 360 + 360) % 360;
	}
	cout<<sum<<endl;
}

C

构造最小长度的包含K个长度为n的不同子串的字符串,用m的字符集。 K <= min(MN,100000)

结论:
可以用长度为n + k - 1 的串构造,恰好同时是上下界
证明过程:
打表发现可行。
然后构造的时候发现可以把长度为n - 1的串当成点,最后一个字符看成边,构成一张有向图,这张图每个点恰好有m条出边和入边,所以一定存在欧拉回路。而欧拉回路的每条边代表一个字符串。
但是这还只是开始:
当n很大的时候,图的点数是MN,必须动态开点。
然后我就犯了很sb的错误:欧拉回路跑一半return。这样显然是错的。因为如果只跑一班,会导致环不完整,即不该相邻的点相邻。----这东西样例都过不了,但是最后调试的时候太慌,没有静心思考为什么错了。导致最后半个小时都在无意义修改。
但是我们发现这个欧拉回路是可以调整的:lyk的解决错误的方法的确很妙,我总是草率的觉得一个算法有问题就肯定不行,其实可以改进!
我们观察后发现欧拉回路的不完整的只有一个环,并且不完整的欧拉回路一定恰好有一个出度>入度的点。
我们可以把这个点当作起点,先跑完整的环,最后加入不完整的部分,于是我们得到一条合法的欧拉路径,就可以作为答案
细节:
因为每个点代表的串长度很大,要用hash或者string,动态开点。并且要记录这个点是什么需要在dfs的时候开一个数组记录边。
找欧拉路的时候,找最后一个点作为起点是不同的,要区分开。
最后欧拉路是几段路径拼起来,注意顺序。
这道题最后还是对拍才发现错误----没有区分入度>出度的点和出度>入度的点。思考得不够仔细!

题解给出的做法:
在n>40的时候随机----这是一个很好的思路,虽然这道题用不上。
n小的时候用Hierholzer’s algorithm求欧拉回路

#include<bits/stdc++.h>
using namespace std;

#define rep(i,l,r) for (int i = l ; i <= (int)r ; i++)
#define repd(i,r,l) for (int i = r ; i >= l; i --)
#define fore(i,x) for (int i = head[x]; i ; i = e[i].next)
#define rvc(i,S) for (int i = 0 ; i < (int)S.size() ; i++)
#define pb push_back
typedef long long ll;
typedef long long ull;

const int maxn = 2000020;
const int inf = 1e8;

ull base = 907;
ull mod = 1e9 + 7;
int n,m,k;
map <ull,int> num;
int vis[maxn][12];
int ans[maxn],tot;
int cnt,b[maxn],cnt2,c[20],z[20],deg[maxn];
ull a[maxn],pow_[maxn];
int st[20],id,ans2[maxn],tot2,flag;

ull gethash(ull cur,int i,int last){
	cur = (cur - last * pow_[n - 2] % mod + mod) % mod;
	cur = (cur * base + i) % mod;
	return cur;
}
void dfs(int x){
//	cout<<x<<" : ";
//	repd(i,n - 1,1) cout<<b[cnt2 - i + 1] + 1;
//	cout<<endl;
	k--;
	if ( k == -1 ){
	   	flag = 1;
		return;
	}
//	if ( tot >= k ) return;
	rep(i,0,m - 1){
		if ( vis[x][i] ) continue;
//		cout<<x<<" "<<i<<endl;
		vis[x][i] = 1;
		ull to = gethash(a[x],i,b[cnt2 - n + 2]);
		b[++cnt2] = i;
		int &d = num[to];
		if ( !d ) d = ++cnt , a[cnt] = to;
		deg[x]++ , deg[d]--;
		dfs(d);
		--cnt2;
		if ( flag ){
			ans2[++tot2] = i;
		}
		else{
			ans[++tot] = i;
		}
		if ( flag && deg[x] == 1 ){
			id = tot;
			rep(i,1,n - 1) st[i] = b[cnt2 + i - (n - 1)];
		//	cout<<"st : \n";
		//	rep(i,1,n - 1) cout<<st[i] + 1;
		//	cout<<endl;
			flag = 0;
		}
		if ( k == -1 ) return;
	}	
}
int main(){
//	freopen("input.txt","r",stdin);
	scanf("%d %d %d",&n,&m,&k);
	rep(i,1,m){
		scanf("%d",&c[i]);
		z[i - 1] = c[i];
	}
	if ( n == 1 ){
		rep(i,1,k) printf("%d",z[i - 1]);
		puts("");
		return 0;
	}
/*	if ( n > 40 ){
		srand(20000907);
		rep(i,1,k + n - 1) printf("%d",z[rand() % m]);
		puts("");
		return 0;
	}*/
	pow_[0] = 1;
	rep(i,1,n) pow_[i] = pow_[i - 1] * base % mod;
	rep(i,1,n - 1) b[++cnt2] = 0;
	a[++cnt] = 0 , num[0] = 1 , dfs(1);
//	rep(i,1,tot2) cout<<ans2[i] + 1;
//	cout<<endl;
	if ( !id ){
		rep(i,1,tot2) ans[++tot] = ans2[i];
		rep(i,1,n - 1) ans[++tot] = 0;
		repd(i,tot,1) printf("%d",z[ans[i]]);
	}
	else{
		rep(i,id + 1,tot) ans2[++tot2] = ans[i];
		rep(i,1,id) ans2[++tot2] = ans[i];
		rep(i,1,n - 1) ans2[++tot2] = st[n - i];
		repd(i,tot2,1) printf("%d",z[ans2[i]]);
	}
	puts("");
//	cout<<n<<endl;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值