E. Masha-forgetful
题意:
给定一个长度为m的字符串s,再给定n个字符串,要求将s分割成长度大于等于2的一些子串,且这些子串在给定的n个字符串中,问应该如何分割,答案输出分割出来的每一段在n个字符串中的位置。
思路:
任何字符串都可以用长度为2,3的子串拼起来,因此我们记录下来给定的n个字符串中所有长度为2,3的子串,这里有一个小技巧,记录长度为2,3的子串时,我们可以像哈希一样将其看成一个10进制数,然后就可以用一个数来表示一个子串,然后我们用
d
p
[
i
]
dp[i]
dp[i]表示从第0至第i位是否已经找到合适的子串。
代码如下:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>
using namespace std;
using tp=tuple<int,int,int>;//用tuple代替结构体
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 10;
void solve()
{
int n,m;cin>>n>>m;
map<int,tp>mp[2];//用map存下来长度为2和3,其子串的权值的位置
for(int i=1;i<=n;i++)
{
string s;cin>>s;s=" "+s;
for(int j=1;j<=m-1;j++)
{
int p=(s[j]-'0')*10+(s[j+1]-'0');//小技巧,用数字表示子串,跟哈希一样的思想
mp[0][p]={j,j+1,i};
if(j<=m-2)//记录长度为3的子串
{
int p=(s[j]-'0')*100+(s[j+1]-'0')*10+(s[j+2]-'0');
mp[1][p]={j,j+2,i};
}
}
}
string s;cin>>s;s=" "+s;
vector<int>dp(m+1,0),pre(m+1);//pre记录以该位置结尾的子串是被分割成长度为2还是3的子串
dp[0]=1;//dp边界初始化
for(int i=2;i<=m;i++)
{
int p1=(s[i-1]-'0')*10+(s[i]-'0');
if(dp[i-2]&&mp[0].count(p1))
{
dp[i]=1;
pre[i]=2;
}
if(i>=3)
{
int p2=(s[i-2]-'0')*100+(s[i-1]-'0')*10+(s[i]-'0');
if(dp[i-3]&&mp[1].count(p2))
{
dp[i]=1;
pre[i]=3;
}
}
}
if(dp[m])//从后往前回溯,记录方案
{
vector<tp>ans;
for(int i=m;i>=1;i-=pre[i])
{
if(pre[i]==2)
{
int p1=(s[i-1]-'0')*10+(s[i]-'0');
ans.push_back(mp[0][p1]);
}
else
{
int p2=(s[i-2]-'0')*100+(s[i-1]-'0')*10+(s[i]-'0');
ans.push_back(mp[1][p2]);
}
}
reverse(ans.begin(),ans.end());
cout<<ans.size()<<"\n";
for(auto [l,r,i]:ans)cout<<l<<" "<<r<<" "<<i<<"\n";
}
else cout<<-1<<"\n";
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
{
solve();
}
return 0;
}
G. MinOr Tree
题意:
给定n个点,m条边,求所有权值按位或的最小生成树。
思路:
我们可以从高位向低位枚举,去掉该位为1的边后,图是否能连通,如果能连通,那么这一位就不在答案中,否则这一位就在答案中
判断连通可以用并查集
代码如下:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 10;
struct edge
{
int u,v,w;
};
int n,m;
vector<edge>edg;
vector<bool>st;
vector<int>f;
int find(int x)
{
if(x!=f[x])f[x]=find(f[x]);
return f[x];
}
bool check(int x)
{
f.resize(n+1);
for(int i=1;i<=n;i++)f[i]=i;
for(int i=1;i<=m;i++)
{
if(!st[i]||edg[i].w>>x&1)continue;
int xx=find(edg[i].u),yy=find(edg[i].v);
if(xx!=yy)f[xx]=yy;
}
for(int i=1;i<=n;i++)
if(find(f[i])!=find(1))return false;
return true;
}
void solve()
{
cin>>n>>m;
edg.resize(m+1);
st.resize(m+1);
for(int i=1;i<=m;i++)
{
int u,v,w;cin>>u>>v>>w;
edg[i]={u,v,w};
st[i]=1;
}
int ans=0;
for(int i=30;i>=0;i--)
{
if(check(i))//判断少了含这一位的边后图是否连通
{
for(int j=1;j<=m;j++)
if(edg[j].w>>i&1)st[j]=0;
}
else ans+=(1<<i);
}
cout<<ans<<"\n";
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
{
solve();
}
return 0;
}