C
题目大意:给定一个树,给出两个节点u,v,输出u,v之间的简单路径.
直接dfs即可,中间记录每个节点的父节点,然后根据其父节点一步步找。
具体细节见代码。
code:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
const int N=4e5+10;
const double eps=1e-4;
int h[N],e[N],ne[N],idx;
int p[N];
int x,y,n;
void add(int a,int b){
e[idx]=b; ne[idx]=h[a]; h[a]=idx++;
}
void dfs(int u,int fa)
{
if(u==x){//找到直接输出
int t=u;
while(p[t])
{
cout<<t<<" ";
t=p[t];
}
cout<<y<<endl;
return ;
}
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(j==fa) continue;
p[j]=u;
dfs(j,u);
}
}
void work()
{
memset(h,-1,sizeof h);
cin>>n>>x>>y;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
add(u,v); add(v,u);
}
dfs(y,-1); //倒着做dfs,可以实现正序输出
}
signed main()
{
int t;
//cin>>t;
t=1;
while(t--)
{
work();
}
return 0;
}
D
题目大意:共有n个石头,给定一个序列A, 甲乙两人轮流拿石头,甲先手,规则是若当前剩有k个石头,只能拿取a[j]个石头,a[j]<=k,问甲最多能拿到多少石头。
思路:刚开始根本没思路,后面看了题解好像是博弈DP,把状态表示好就很容易做好此题。 具体做法见代码。
code:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
const int N=2e5+10;
const double eps=1e-4;
int n,k;
int a[N];
int dp[N];
void work()
{
cin>>n>>k;
for(int i=1;i<=k;i++){
cin>>a[i];
}
//dp[i]表示当前共取i个石头,先手获得的最大值
//(先手(T)最后一次取的石头为a[j])
//则先手取前,后手(A)是先手,此时共有i-a[j]个石头
//因此先手(T)可以取的石头为a[j]+i-a[j]+dp[i-a[j]]
//状态转移为dp[i]=max(dp[i],a[j]+i-a[j]+dp[i-a[j]])
for(int i=0;i<=n;i++){
for(int j=1;j<=k;j++){
if(i>=a[j]) dp[i]=max(dp[i],a[j]+i-a[j]-dp[i-a[j]]);
}
}
cout<<dp[n]<<endl;
}
signed main()
{
int t;
//cin>>t;
t=1;
while(t--)
{
work();
}
return 0;
}
E
题目大意:
有n堆苹果围成一个环,每堆苹果有
a
i
a_i
ai个,若当前在 i 处,如果当前有苹果则取走一个苹果,如果没有则不取,然后都移向 i+1 处,问当取走K个苹果后,每堆苹果还剩多少个。
思路: 直接二分要循环的次数,也就是重复经过每堆苹果多少次,然后计算答案即可。
code:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
const int N=4e5+10;
const double eps=1e-4;
int n,k;
int a[N];
int get(int x)
{
int ans=0;
for(int i=1;i<=n;i++){
if(a[i]>=x) ans+=x;
else ans+=a[i];
}
return ans;
}
void work()
{
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int l=1,r=k;
while(l<r)
{
int mid=l+r>>1;
if(get(mid)<k) l=mid+1;
else r=mid;
}
int x=get(r)-k;
//cout<<x<<endl;
for(int i=1;i<=n;i++){
a[i]-=r;
}
for(int i=n;x>0;i--){
if(a[i]>=-1) {
a[i]++;
x--;
}
}
for(int i=1;i<=n;i++){
if(a[i]>0) cout<<a[i]<<" ";
else cout<<0<<" ";
}
cout<<endl;
}
signed main()
{
int t;
//cin>>t;
t=1;
while(t--)
{
work();
}
return 0;
}
F
题目大意:有n个点,在第 i 个点建机场要花
x
i
x_i
xi元,建码头要花
y
i
y_i
yi元。在
A
i
A_i
Ai和
B
i
B_i
Bi两点修建道路需要
z
i
z_i
zi元。
若两点都有机场或码头或一个有机场一个有码头或有道路连接,则这两点可以互通。
问最少需要多少钱可以实现n点互通。
思路:做法很明显是最小生成树,但是关键怎么建图(连边),一个巧妙地方法是,建立两个虚拟源点,n+1,n+2, 然后每个点的机场造价相当于往n+1这个点连边的边权,同理,每个点的码头造价相当于往n+2这个点连边的边权。 然后跑四遍最小生成树算法即可(无机场码头,无机场有码头,无码头有机场,都有)。
code:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
const int N=4e5+10;
const double eps=1e-4;
int n,m;
int a[N],b[N],p[N];
int x[N],y[N],z[N];
struct node{
int x,y,z;
bool operator <(const node &t) const
{
return z<t.z;
}
}e[N];
int cnt;
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
void init()
{
cnt=0;
for(int i=1;i<=n+2;i++) p[i]=i;
}
void merge(int x,int y)
{
int px=find(x),py=find(y);
p[px]=py;
}
int kru(int nx)
{
sort(e+1,e+1+cnt);
int ans=0,num=0;
for(int i=1;i<=cnt;i++){
int x=e[i].x,y=e[i].y;
if(find(x)!=find(y)) {
merge(x,y);
ans+=e[i].z;
num++;
if(num==nx-1) return ans;
}
}
return 1e18;
}
void work()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<=m;i++){
cin>>x[i]>>y[i]>>z[i];
}
int ans=1e18;
for(int k=0;k<=3;k++){
init();
for(int i=1;i<=m;i++) e[++cnt]={x[i],y[i],z[i]};
if(!k) ans=min(ans,kru(n));
if(k==1){
for(int i=1;i<=n;i++){
e[++cnt]={i,n+1,a[i]};
}
ans=min(ans,kru(n+1));
}
if(k==2){
for(int i=1;i<=n;i++){
e[++cnt]={i,n+2,b[i]};
}
ans=min(ans,kru(n+1));
}
if(k==3){
for(int i=1;i<=n;i++){
e[++cnt]={i,n+1,a[i]};
}
for(int i=1;i<=n;i++){
e[++cnt]={i,n+2,b[i]};
}
ans=min(ans,kru(n+2));
}
}
cout<<ans<<endl;
}
signed main()
{
int t;
//cin>>t;
t=1;
while(t--)
{
work();
}
return 0;
}