GYM104081 部分题解

比赛链接:https://codeforces.com/gym/104081

目前就做了 8 题,里面还有 4 个水题……
水题:ACEG,模拟题意即可,C 和 E 有一些细节。不想写题解了

F
首先目标是如何将这 9 个数分组,由于答案一定存在,考虑随机化,固定 \(a_1 \in S_1\),然后随机一个 \(a_i \in S_1\),异或得到 \(S_1\) 的另一个元素,同理第二个集合只需要随机两个。作为可以证明期望 20 次内就可以完成分组。
完成分组之后如何确定 \(a,b,c\) ?首先每组最大的一定是 \(a|b\),但是 xor 和 and 的大小不好确定,直接 \(2^3\) 暴力枚举 xor/and。
不妨看每一个二进制位,如果 or 为 0,那么肯定两个数该位都是 0,and 为 1,则都为 1. 统计答案即可
代码:

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f;

mt19937 mrand(random_device{}()); 
int rnd(int x) { return mrand() % x;}
int rnd(int l,int r){return rnd(r-l+1) + l;} 

ll a[15],ansp,ansq,ansr;

int ck(vector<ll>d[3]){
//	a|b b|c c|a
//	a&b b&c c&a
	ll p,q,r;
	p=q=r=0;
	for(int i=60;i>=0;i--){
		vector<int>conf(3,-1);
		for(int j=0;j<3;j++){
			if(((d[j][0] >> i)&1) == 0)conf[j] = conf[(j+1)%3] = 0;
			if(((d[j][1] >> i)&1) == 1)conf[j] = conf[(j+1)%3] = 1;
		}
		for(int j=0;j<3;j++)
			if(conf[j] == -1)conf[j] = conf[(j+1)%3] ^ 1;
		if(conf[0]==1)p |= 1ll<<i;
		if(conf[1]==1)q |= 1ll<<i;
		if(conf[2]==1)r |= 1ll<<i;
	}
	if((p|q)==d[0][0]&&(p&q)==d[0][1]&&(q|r)==d[1][0]&&(q&r)==d[1][1]&&(r|p)==d[2][0]&&(r&p)==d[2][1]){
		ansp=p,ansq=q,ansr=r;
		return 1;
	}
	return 0;
}

void solve(){
	map<ll,int>is;
	for(int i=1;i<=9;i++)scanf("%lld",&a[i]),is[a[i]]++;
	while(1){
		int a02,b01,b02;
		ll a2,b1=-1,b2=-1;
		a02=rnd(2,9);
		a2 = a[a02];
		do{b01=rnd(2,9);}while(b01 == a02);
		do{b02=rnd(2,9);}while(b02 == a02 || b02 == b01);
		b1=a[b01],b2=a[b02];
		
		map<ll,int>is2;
		is2 = is;
		-- is2[b1], -- is2[a[1]], -- is2[a2], -- is2[b2];
		if(-- is2[b1^b2] < 0){
			continue;
		}
		if(-- is2[a[1]^a2] < 0){
			continue;
		}
		vector<ll>c[3],d[3];
		c[0].pb(a[1]),c[0].pb(a2),c[0].pb(a[1]^a2);
		c[1].pb(b1),c[1].pb(b2),c[1].pb(b1^b2);
		for(auto &u : is2)while(u.second){
			c[2].pb(u.first);
			-- u.second;
		}
		
		for(int i=0;i<3;i++)sort(c[i].begin(),c[i].end(),greater<ll>());
		for(int i=0;i<=1;i++){
			d[0]=c[0];
			if(i==1)swap(d[0][1],d[0][2]);
			for(int j=0;j<=1;j++){
				d[1]=c[1];
				if(j==1)swap(d[1][1],d[1][2]);
				for(int k=0;k<=1;k++){
					d[2]=c[2];
					if(k==1)swap(d[2][1],d[2][2]);
					
					int t=ck(d);
					if(t==1){
						printf("%lld %lld %lld\n",ansp,ansq,ansr);
						return ;
					}
				}
			}
		}
	}
}

signed main(){
	int te;scanf("%d",&te);
	while(te --)solve();

	return 0;
}

H
带步数限制的 dijkstra ,每次多记一个"步数",其它完全一致

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 505;

int n,m;
int a[505][505];
vector<pii>g[505];
int ed,vis[maxn][maxn];
ll w[505];
ll dis[maxn][maxn];
struct node{
	int cs;
	ll dis;
	int now;
};

bool operator < (node a,node b){
	if(a.cs != b.cs)return a.cs > b.cs;
	return a.dis>b.dis;
}

void dijkstra(){
	memset(dis,0x3f,sizeof dis);
	priority_queue<node>pq;
	pq.push(node{0,0,1});
	dis[1][0] = 0;
	
	while(!pq.empty()){
		node now = pq.top();pq.pop();
		int u = now.now, cs =now.cs;
		if(vis[u][cs])continue;
		vis[u][cs] = 1;
		
		for(auto v : g[u]){
			if(cs+1 <= n-1 && dis[u][cs] + v.second < dis[v.first][cs+1]){
				dis[v.first][cs + 1] = dis[u][cs] + v.second;
				pq.push(node{cs+1, dis[v.first][cs+1], v.first});
			}
		}
	}
}

signed main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int x,y,z;scanf("%d%d%d",&x,&y,&z);
		g[x].pb(mpr(y,z));
		g[y].pb(mpr(x,z));
	}
	
	dijkstra();
	int qu;scanf("%d",&qu);
	for(int i=1;i<=qu;i++){
		scanf("%d",&ed);
		for(int j=1;j<=n-1;j++){
			scanf("%lld",&w[j]);
		}
		for(int j=2;j<=n-1;j++)w[j] += w[j-1];
		ll ans = 1e18;
		for(int j=1;j<=n-1;j++)
			ans = min(ans, dis[ed][j] + w[j]);
		printf("%lld\n",ans);
	}
	
	return 0;
}

I
一眼 dp,设 \(dp[i][0/1]\) 表示当前考虑到字符串第 \(i\) 位,上一个字符串是在 a/b 集合时的最优解。
转移枚举位置,用 hash/trie 判断这段字符串是否在 b/a 集合里即可。
我写了 trie,不过 hash 更简单。

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 5e5+5;

int n,m;
char s[maxn],t[maxn];
struct trie{
	int tr[maxn][26];
	int vis[maxn * 5];
	int cnt=0;
	void insert(char *t){
		int len = strlen(t+1);
		int now = 0;
		for(int i=1;i<=len;i++){
			if(!tr[now][t[i] - 'a']){
				tr[now][t[i] - 'a'] = ++cnt;
			}
			now = tr[now][t[i] - 'a'];
		}
		vis[now] = len;
	}
}tr[2];

vector<int> query(char *t,int id,int bs){
	int nt=strlen(t+1);
	int now = 0;
	vector<int>vc;
	for(int i=id;i<=nt;i++){
		if(!tr[bs].tr[now][t[i] - 'a'])return vc;
		now = tr[bs].tr[now][t[i] - 'a'];
		if(tr[bs].vis[now]){
			vc.pb(tr[bs].vis[now]);
		}
	}
	return vc;
}

int dp[maxn][2];
signed main(){
	memset(dp,0x3f,sizeof dp);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%s",s + 1);
		tr[0].insert(s);
	}
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		scanf("%s",s + 1);
		tr[1].insert(s);
	}
	
	scanf("%s",t+1);
	int nt=strlen(t+1);
	dp[0][0] = dp[0][1] = 0;
	for(int i=1;i<=nt;i++){
		vector<int>v = query(t,i,1);
//		printf("! %d %d\n",i,v.size());
//		if(v.size())printf("?? %d\n",v[0]);
		for(int u : v)
			dp[i+u-1][1] = min(dp[i+u-1][1], dp[i-1][0] + 1);
		v = query(t,i,0);
		for(int u : v)
			dp[i+u-1][0] = min(dp[i+u-1][0], dp[i-1][1] + 1);
	}
//	printf("? %d\n",dp[4][0]);
	if(dp[nt][0] == INF && dp[nt][1] == INF)puts("-1");
	else printf("%d\n",min(dp[nt][0], dp[nt][1])); 

	return 0;
}

L
记一下每个结点的 \(k\) 级祖先,可以用倍增实现。反过来我们就得到了每个点子树内所有距离为 \(k\) 的点。
一个点合并子树的时候,可以使用启发式合并的 trick,即永远是小的集合 合并到大的集合中。然后删除掉所有距离为 \(k\) 的点的信息。
对于每个点,由于最多合并了 \(\log\) 次,因此总时间复杂度 \(O(n\log n)\)

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 2e5+5;

int n,k;
int c[maxn],ans[maxn];
vector<int>g[maxn];
map<int,int>res[maxn];
vector<int>del[maxn];
int fa[maxn][21],dep[maxn];
vector<int>qw;

void dfs(int x,int fat=0){
	fa[x][0] = fat;
	dep[x] = dep[fat] + 1;
	for(int u : g[x])if(u!=fat){
		dfs(u,x);
	}
}

void dfs2(int x,int fat=0){
	for(int u : g[x])if(u!=fat){
		dfs2(u,x);
	}
	if(dep[x] > k){
		int v = x;
		for(int j : qw)
			if(k & (1<<j))v = fa[v][j];
		del[v].pb(x);
	}
}

void dfs3(int x,int fat=0){
	res[x][c[x]] ++;
	for(int u : g[x])if(u!=fat){
		dfs3(u,x);
		if(res[x].size() < res[u].size())swap(res[x], res[u]);
		for(auto v : res[u])res[x][v.first] += v.second;
		res[u].clear();
	}
	
	ans[x] = res[x].size();
	for(int v : del[x]){
		res[x][c[v]] --;
		if(res[x][c[v]] == 0)res[x].erase(c[v]);
	}
}

signed main(){
	scanf("%d%d",&n,&k);
	for(int i=19;i>=0;i--)
		if(k&(1<<i))qw.pb(i);
		
	for(int i=1;i<=n;i++)scanf("%d",&c[i]);
	for(int i=1;i<n;i++){
		int x,y;scanf("%d%d",&x,&y);
		g[x].pb(y), g[y].pb(x);
	}
	dfs(1);
	for(int j=1;j<=19;j++)
		for(int i=1;i<=n;i++)
			fa[i][j] = fa[fa[i][j-1]][j-1];
	dfs2(1);
	dfs3(1);
	
	int qu;scanf("%d",&qu);
	while(qu--){int x;scanf("%d",&x);printf("%d\n",ans[x]);}

	return 0;
}
posted @ 2023-04-19 20:32  SkyRainWind  阅读(22)  评论(0编辑  收藏  举报