ABC416
D - Match, Mod, Minimize 2
简单题
要使最终结果最小,要尽可能的使a[i]+b[i]>=m,从而使得%m变小
因为a[i],b[i]<m,所以ai+bi最多只能减去1个m
具体的,当ai+bi<m时,(ai+bi)%m=ai+bi
当ai+bi>=m时,(ai+bi)%m=ai+bi-m
故考虑将a从大到小排序,b从小到大排序,双指针维护最多能减多少个m(假设是k个)
答案就是sum{ai}+sum{bi}-k*m
时间复杂度O(n)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
#define jiaa(a,b) {a+=b;if(a>=MOD) a-=MOD;}
#define jian(a,b) {a-=b;if(a<0) a+=MOD;}
using namespace std;
int ksm(int a,int b,int p){
if(b==0) return 1;
if(b==1) return a%p;
int c=ksm(a,b/2,p);
c=c*c%p;
if(b%2==1) c=c*a%p;
return c%p;
}
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int a[300005],b[300005];
signed main()
{
//freopen("filename.in", "r", stdin);
//freopen("filename.out", "w", stdout);
int T=read();
while(T--){
int n=read(),m=read();
int sum=0;
for(int i=1;i<=n;i++){
a[i]=read();
sum+=a[i];
a[i]*=-1;//这里a数组取反,sort就是从大到小排序
}
for(int i=1;i<=n;i++){
b[i]=read();
sum+=b[i];
}
sort(a+1,a+n+1);
sort(b+1,b+n+1);
int l=1,r=1;
while(l<=n&&r<=n){
if(b[r]-a[l]>=m){//这里由于a取反了,故为-a[l]
l++;
r++;
sum-=m;
}
else r++;
}
cout<<sum<<'\n';
}
return 0;
}
E - Development
首先想到最短路
发现若没有飞机,就是类似floyd的算法
操作一直接仿floyd修改最短路,强制要求走x->y/y->x这条边即可
若有飞机,记录每个点到任意机场的最短路径
则最终两点最短路一定是floyd算的最短路(不坐飞机)与两点到任意机场的最短路之和+T(坐飞机花T的时间)的最小值
特判无解情况即可
本题思路较简单,但具体实现、代码难写点,本人卡了2h(雾
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
#define jiaa(a,b) {a+=b;if(a>=MOD) a-=MOD;}
#define jian(a,b) {a-=b;if(a<0) a+=MOD;}
using namespace std;
int ksm(int a,int b,int p){
if(b==0) return 1;
if(b==1) return a%p;
int c=ksm(a,b/2,p);
c=c*c%p;
if(b%2==1) c=c*a%p;
return c%p;
}
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int fl[505][505];
int d[505],vis[505],pla[505];
signed main()
{
//freopen("filename.in", "r", stdin);
//freopen("filename.out", "w", stdout);
int n=read(),m=read();
memset(fl,0x3f,sizeof(fl));
for(int i=1;i<=n;i++) fl[i][i]=0;
for(int i=1;i<=m;i++){
int u=read(),v=read(),w=read();
fl[u][v]=min(fl[u][v],w);
fl[v][u]=min(fl[v][u],w);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
fl[j][k]=min(fl[j][k],fl[j][i]+fl[i][k]);
}
}
}
for(int i=1;i<=n;i++) pla[i]=1e18;
int K=read(),T=read();
for(int i=1;i<=K;i++){
d[i]=read();
vis[d[i]]=1;
pla[d[i]]=0;
}
for(int i=1;i<=n;i++){
if(vis[i]) continue;
for(int j=1;j<=K;j++){
pla[i]=min(pla[i],fl[i][d[j]]);
}
}
int q=read();
while(q--){
int opt=read();
if(opt==1){
int x=read(),y=read(),z=read();
fl[x][y]=min(fl[x][y],z);
fl[y][x]=min(fl[y][x],z);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
fl[i][j]=min(fl[i][j],min(fl[i][x]+fl[x][y]+fl[y][j],fl[i][y]+fl[y][x]+fl[x][j]));
}
}
for(int i=1;i<=n;i++){
if(vis[i]) continue;
pla[i]=1e18;
for(int j=1;j<=K;j++){
pla[i]=min(pla[i],fl[i][d[j]]);
}
}
}
else if(opt==2){
int x=read();
d[++K]=x;
vis[x]=1;
pla[x]=0;
for(int i=1;i<=n;i++){
if(vis[i]) continue;
pla[i]=min(pla[i],fl[i][x]);
}
}
else{
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(fl[i][j]==4557430888798830399&&(pla[i]==1000000000000000000||pla[j]==1000000000000000000)) continue;
ans+=min(fl[i][j],pla[i]+pla[j]+T);
}
}
cout<<ans<<'\n';
}
}
return 0;
}
F - Paint Tree 2
树形DP
题目其实就是求K条不相交的路径使得路径上的点权之和尽可能多
考虑每个子树根节点的状态
其实只有3种:
1.子树的最大值与根节点无关
2.子树的最大值过根且未闭合(即与父亲的边可以加入路径,子树内一条路径过根然后延伸到子树外)
3.子树的最大值过根且已闭合(即子树内一条路径过根然后延伸到子树内)
于是状态有了:dp[u][i][0/1/2]
(要在记一维记录选了几条路径,类似背包)
分情况转移即可
注意背包的枚举顺序有要求,故可以另开一个数组

浙公网安备 33010602011771号