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
鸽巢原理,不记了
本文来自博客园,作者:Qiansui,转载请注明原文链接:https://www.cnblogs.com/Qiansui/p/17564281.html