A.牛牛爱字符串
注意原字符串有空格,不要用cin
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
IO;
int T=1;
//cin>>T;
while(T--)
{
string s;
while(getline(cin,s))
{
vector<string> ans;
int n=s.size();
for(int i=0;i<n;i++)
{
if(s[i]<'0'||s[i]>'9') continue;
string now="";
while(i<n&&s[i]>='0'&&s[i]<='9')
{
now+=s[i];
i++;
}
reverse(now.begin(),now.end());
while(now.size()>1&&now.back()=='0') now.pop_back();
reverse(now.begin(),now.end());
ans.push_back(now);
}
for(auto t:ans) cout<<t<<' ';
cout<<'\n';
}
}
return 0;
}
B.牛牛爱位运算
&
只会越运算越小,直接选择最大的数即可
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100010;
int a[N],n;
int main()
{
IO;
int T=1;
cin>>T;
while(T--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n);
cout<<a[n]<<'\n';
}
return 0;
}
C.牛牛爱博弈
打表找规律,懂得都懂
最终只要
n
n
n不是
3
3
3的倍数先手必胜
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_set>
using namespace std;
const int N=110;
int sg[N];
// int dfs(int u)
// {
// if(sg[u]!=-1) return sg[u];
// unordered_set<int> mp;
// for(int i=0;(1<<i)<=u;i++) mp.insert(dfs(u-(1<<i)));
// for(int i=0;;i++)
// if(!mp.count(i)) return sg[u]=i;
// }
int main()
{
IO;
int T=1;
cin>>T;
memset(sg,-1,sizeof sg);
while(T--)
{
cin>>n;
if(n%3) cout<<"Alan\n";
else cout<<"Frame\n";
}
// for(int i=0;i<=100;i++)
// {
// cout<<i<<' ';
// if(dfs(i)) cout<<"Alan\n";
// else cout<<"Frame\n";
// }
return 0;
}
D. 牛妹爱数列
f
[
i
]
[
0
/
1
]
f[i][0/1]
f[i][0/1]表示:考虑前
i
i
i个序列,把他们全变成
0
/
1
0/1
0/1的最小翻转次数
转移:考虑最后一步是单点修改还是前缀修改。
如果是单点修改
f
[
i
]
[
1
]
=
m
i
n
(
f
[
i
]
[
1
]
,
f
[
i
−
1
]
[
1
]
+
1
−
a
[
i
]
)
,
f
[
i
]
[
0
]
=
m
i
n
(
f
[
i
]
[
0
]
,
f
[
i
−
1
]
[
0
]
+
a
[
i
]
)
f[i][1]=min(f[i][1],f[i-1][1]+1-a[i]),f[i][0]=min(f[i][0],f[i-1][0]+a[i])
f[i][1]=min(f[i][1],f[i−1][1]+1−a[i]),f[i][0]=min(f[i][0],f[i−1][0]+a[i])
如果是前缀修改
f
[
i
]
[
1
]
=
m
i
n
(
f
[
i
]
[
1
]
,
f
[
i
−
1
]
[
0
]
+
a
[
i
]
+
1
)
,
f
[
i
]
[
0
]
=
m
i
n
(
f
[
i
]
[
0
]
,
f
[
i
−
1
]
[
1
]
+
1
−
a
[
i
]
+
1
)
f[i][1]=min(f[i][1],f[i-1][0]+a[i]+1),f[i][0]=min(f[i][0],f[i-1][1]+1-a[i]+1)
f[i][1]=min(f[i][1],f[i−1][0]+a[i]+1),f[i][0]=min(f[i][0],f[i−1][1]+1−a[i]+1)
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100010;
int f[N][2];
int a[N],n;
int main()
{
IO;
int T=1;
//cin>>T;
while(T--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
memset(f,0x3f,sizeof f);
f[0][0]=f[0][1]=0;
for(int i=1;i<=n;i++)
{
f[i][0]=min(f[i-1][0]+a[i],f[i-1][1]+1-a[i]+1);
f[i][1]=min(f[i-1][0]+1-a[i]+1,f[i-1][1]+1-a[i]);
}
cout<<f[n][0]<<'\n';
}
return 0;
}
E.牛妹游历城市
暴力建图边数达到恐怖的
n
2
n^2
n2不光空间开不下,而且时间跑不过,不可取。
考虑优化建图:建立
32
32
32个虚拟源点(编号
n
+
1
…
n
+
32
)
n+1 \dots n+32)
n+1…n+32)如果一个点
i
i
i的
a
i
a_i
ai的二进制表示中第
j
(
0
≤
j
≤
31
)
j(0\leq j\leq 31)
j(0≤j≤31)位是1,那么连一条边
i
−
>
n
+
j
+
1
i->n+j+1
i−>n+j+1权值是
1
<
<
j
1<<j
1<<j的边,并且连一条
n
+
j
+
1
−
>
i
n+j+1->i
n+j+1−>i权值是
0
0
0的边。然后不难发现原图中的所有边都可以转化成新图的边,同样新图的所有边都能转化成原图的边。 但是并不影响求最短路比如权值是101011
和101010
的两个点,我们会建立边权是10、1000、100000
这些路径,但是发现这些相当于重边,不会影响答案。
博主的图非常清楚直接秒懂
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<ll,int> pli;
const int N=111010,M=1000010;
const ll INF=0x3f3f3f3f3f3f3f3f;
int h[N],e[M],ne[M],idx;
ll w[M],a[N];
ll dist[N];
bool st[N];
int n;
void add(int a,int b,ll c)
{
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
void dijkstra()
{
memset(dist,0x3f,sizeof dist);
memset(st,0,sizeof st);
dist[1]=0;
priority_queue<pli,vector<pli>,greater<pli>> q;
q.push({0,1});
while(q.size())
{
int t=q.top().second;q.pop();
if(st[t]) continue;
st[t]=1;
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+w[i])
{
dist[j]=dist[t]+w[i];
q.push({dist[j],j});
}
}
}
}
int main()
{
IO;
int T=1;
cin>>T;
while(T--)
{
cin>>n;
memset(h,-1,sizeof h);
idx=0;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++)
for(int j=0;j<32;j++)
if(a[i]>>j&1)
{
add(i,j+n+1,1ll<<j);
add(j+n+1,i,0ll);
}
dijkstra();
if(dist[n]==INF) cout<<"Impossible\n";
else cout<<dist[n]<<'\n';
}
return 0;
}
学习ST表,F明天补了
F.牛妹的苹果树
方法一:线段树(动态)+欧拉序ST表求lca
首先有一个引理:树的直径具有可合并的性质,同一棵树上两个区间的直径的两个端点分别为
(
a
,
b
)
(a,b)
(a,b),
(
c
,
d
)
(c,d)
(c,d),那么合并两个区间后的新的直径的端点一定在
a
,
b
,
c
,
d
{a,b,c,d}
a,b,c,d中,通过枚举端点计算它们的距离(6种情况),取最大值可以得到两个区间合并的直径。 其正确性证明和两遍dfs求树的直径的证明过程类似。
由此我们考虑线段树维护区间直径即可。求lca最好用欧拉序+ST表的,这个我不会可以借鉴大佬写法线段树+ST表求lca
方法二:ST表维护直径(静态)+ST表求lca
由于本题为区间静态问题,并且直径有区间可重复计算的性质,考虑用ST表维护直径ST表维护直径
// O(nlogn)
#include<iostream>
#include<algorithm>
#include<cstring>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=300010,M=2*N;
int h[N],e[M],ne[M],idx;
ll w[M];
ll dist[N];
int dfn[N],eular[M],ST[M][20],cnt;
int lg[M];
pii f[N][20];
int n,q;
void add(int a,int b,ll c)
{
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
void dfs(int u,int fa)
{
dfn[u]=++cnt;
ST[cnt][0]=u;
f[u][0]={u,u};
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(j==fa) continue;
dist[j]=dist[u]+w[i];
dfs(j,u);
ST[++cnt][0]=u;
}
}
int lca(int a,int b)
{
a=dfn[a],b=dfn[b];
if(a>b) swap(a,b);
int len=lg[b-a+1];
return dist[ST[a][len]]<dist[ST[b-(1<<len)+1][len]]?ST[a][len]:ST[b-(1<<len)+1][len];
}
ll calc(int a,int b)
{
int pab=lca(a,b);
return dist[a]+dist[b]-2*dist[pab];
}
void pre()
{
for(int i=2;i<=cnt;i++) lg[i]=lg[i>>1]+1;
for(int j=1;j<=lg[cnt];j++)
for(int i=1;i+(1<<j)-1<=cnt;i++)
ST[i][j]=dist[ST[i][j-1]]<dist[ST[i+(1<<j-1)][j-1]]?ST[i][j-1]:ST[i+(1<<j-1)][j-1];
for(int j=1;j<=lg[n];j++)
for(int i=1;i+(1<<j)-1<=n;i++)
{
pii a=f[i][j-1],b=f[i+(1<<j-1)][j-1];
f[i][j]={a.x,a.y};
if(calc(a.x,b.x)>calc(f[i][j].x,f[i][j].y)) f[i][j]={a.x,b.x};
if(calc(a.x,b.y)>calc(f[i][j].x,f[i][j].y)) f[i][j]={a.x,b.y};
if(calc(a.y,b.x)>calc(f[i][j].x,f[i][j].y)) f[i][j]={a.y,b.x};
if(calc(a.y,b.y)>calc(f[i][j].x,f[i][j].y)) f[i][j]={a.y,b.y};
if(calc(b.x,b.y)>calc(f[i][j].x,f[i][j].y)) f[i][j]={b.x,b.y};
}
}
ll query(int l,int r)
{
int len=lg[r-l+1];
pii a=f[l][len],b=f[r-(1<<len)+1][len];
ll res=calc(a.x,a.y);
res=max(res,calc(a.x,b.x));
res=max(res,calc(a.x,b.y));
res=max(res,calc(a.y,b.x));
res=max(res,calc(a.y,b.y));
res=max(res,calc(b.x,b.y));
return res;
}
int main()
{
memset(h,-1,sizeof h);
cin>>n>>q;
for(int i=1;i<n;i++)
{
int a,b;
ll c;
cin>>a>>b>>c;
add(a,b,c),add(b,a,c);
}
dfs(1,0);
pre();
while(q--)
{
int l,r;
cin>>l>>r;
cout<<query(l,r)<<'\n';
}
return 0;
}