2023杭电多校第一场 - 2 5 9

比赛地址:传送门

1002 树形DP
1005 快速判断俩字符串是否循环同构
1009 鸽巢原理

题解搜集:
https://zhuanlan.zhihu.com/p/644457415

1002 City Upgrading

思路:
树形DP
具体思路可见类似题及题解链接
记得开long long
下为代码:

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*

*/
const ll maxm=1e5+5,inf=0x3f3f3f3f3f3f3f3f,mod=998244353;
int n, a[maxm];
ll dp[maxm][3];
vector<vector<int>> e;

void dfs(int u,int fa){
	dp[u][0] = a[u];
	dp[u][1] = dp[u][2] = 0;
	ll pos=inf,sum=0;
	for(auto v : e[u]){
		if(v == fa) continue;
		dfs(v, u);
		ll t=min(dp[v][0], dp[v][1]);
		dp[u][0] += min(t, dp[v][2]);
		dp[u][2] += t;
		dp[u][1] += t;
		if(dp[v][0] < dp[v][1]) sum=1;
		else pos = min(pos, dp[v][0] - dp[v][1]);
	}
	if(!sum) dp[u][1] += pos;
	return ;
}

void solve(){
	cin >> n;
	e=vector<vector<int>>(n+1,vector<int>());
	for(int i = 1; i <= n; ++i) cin>>a[i];
	for(int i = 1; i < n; ++i){
		int u, v;
		cin >> u >> v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	dfs(1,0);
	cout << min(dp[1][0], dp[1][1]) << '\n';
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

类似题
洛谷 P2458 保安站岗


1005 Cyclically Isomorphic

思路:
法一:字符串哈希,求每个串的循环同构串的哈希最小值,考虑子串哈希即可。这样即为O(n)
处理子串哈希,O(1)询问
子串哈希再记一记

法二:后缀自动机存双倍原字符,取字典序最小的方式走自动机原字符串长度取到最小表示法,后可利用 map 等标识或哈希加快询问时间复杂度

法三:最小表示法+判断
最小表示法得字典序最小的字符串,存其哈希值,再最后判断 不如直接哈希
或者直接存字典序最小的字符串,暴力判断相等?好像\(O(mq)\)的判断有点水?

下为代码:
法一:字符串哈希

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
哈希冲突?目前无
*/
const int maxm = 2e5+5, inf = 0x3f3f3f3f, mod = 1e9 + 7;
int base = 131;
int n, m;

void solve(){
	cin>>n>>m;
	string ss;
	vector<ll> val(n + 1, inf);
	for(int i = 1; i <= n; ++ i){
		cin >> ss;
		int len = ss.size();
		vector<ll> hash((len << 1) + 1, 0);
		ll p = 1;
		for(int j = 1; j <= (len << 1); ++j){//求子串哈希
			ll t;
			if(j > len) t = ss[j - len - 1] - 'a';
			else t = ss[j - 1] - 'a';
			hash[j] = (hash[j-1] * base + t) % mod;
			if(j > len)
				val[i] = min(val[i], (mod + hash[j]- hash[j - len] * p % mod) % mod);//存子串哈希最小值
			else p = p * base % mod;
		}
	}
	int query;
	cin>>query;
	while(query--){
		int u, v;
		cin >> u >> v;
		if(val[u] == val[v]) cout << "Yes\n";
		else cout << "No\n";
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

法二:后缀自动机 + 判断

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
利用后缀自动机双倍原长+走最小的路 求解字典序最小的循环同构字符串
*/
const int maxm=1e5+5,inf=0x3f3f3f3f,mod=998244353,N=maxm<<1;
int n,m;

int tot=1,np=1;
int fa[N],ch[N][26],len[N],cnt[N];
void insert(int c){
	int p=np;
	np=++tot;
	len[np]=len[p]+1;
	cnt[np]=1;

	for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;

	if(p==0) fa[np]=1;// 1
	else{
		int q=ch[p][c];
		if(len[q]==len[p]+1)// 2
			fa[np]=q;
		else{// 3
			int nq=++tot;
			len[nq]=len[p]+1;
			fa[nq]=fa[q];
			fa[q]=nq;
			fa[np]=nq;
			for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
			memcpy(ch[nq],ch[q],sizeof(ch[q]));
		}
	}
	return ;
}

string findMinimumRepresentation(string str) {
	tot = np = 1;
	for(auto c : str) insert(c - 'a');
	for(auto c : str) insert(c - 'a');
	string res;
	int p = 1;
	for(int i = 0; i < str.size(); ++ i){
		for(int j = 0; j < 26; ++ j){
			if(ch[p][j]){
				p = ch[p][j];
				res += (char)(j + 'a');
				break;
			}
		}
	}
	for(int i = 1; i <= tot; ++ i){
		fa[i] = len[i] = 0;
		mem(ch[i], 0);
	}
	return res;
}

void solve(){
	cin>>n>>m;
	string ss;
	unordered_map<string, int > q;
	vector<int> val(n+1, 0);
	int idx = 1;
	for(int i=1;i<=n;++i){
		cin>>ss;
		ss = findMinimumRepresentation(ss);
		if(!q.count(ss)) q[ss]= idx ++;
		val[i] = q[ss];
	}
	int query;
	cin>>query;
	while(query--){
		int u,v;
		cin>>u>>v;
		if(val[u] == val[v]) cout << "Yes\n";
		else cout << "No\n";
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

法三:最小表示法+判断

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
利用最小表示法求解字典序最小的循环同构字符串
*/
const int maxm=2e5+5,inf=0x3f3f3f3f,mod=998244353;
int n,m;

string findMinimumRepresentation(string str) {
	int n = str.length();
	int i = 0, j = 1, k = 0;
	while (i < n && j < n && k < n) {
		if (str[(i + k) % n] == str[(j + k) % n]) {
			k++;
		} else {
			if (str[(i + k) % n] > str[(j + k) % n]) {
				i = i + k + 1;
			} else {
				j = j + k + 1;
			}
			if (i == j) {
				j++;
			}
			k = 0;
		}
	}
	int start = std::min(i, j);
	return str.substr(start) + str.substr(0, start);
}

void solve(){
	cin>>n>>m;
	vector<string> ss(n+5);
	for(int i=1;i<=n;++i){
		cin>>ss[i];
		ss[i]=findMinimumRepresentation(ss[i]);
	}
	int query;
	cin>>query;
	while(query--){
		int u,v;
		cin>>u>>v;
		if(ss[u] == ss[v]) cout << "Yes\n";
		else cout << "No\n";
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

最小表示法模板:
洛谷 P1368 【模板】最小表示法


1009 Assertion

鸽巢原理,不记了

posted on 2023-07-18 22:09  Qiansui  阅读(218)  评论(0编辑  收藏  举报