T1:排序后二分+贪心(其实可以不二分
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e9
using namespace std;
inline void read(int &x)
{
char c; while(!((c=getchar())>='0'&&c<='9'));
x=c-'0';
while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 210000;
int n,m;
int a[maxn];
inline bool cmp(const int x,const int y){return x<y;}
bool judge(const int mid)
{
int l=1,r=n,t=m;
while(t--)
{
if(a[l]+a[r]<=mid) l++;
r--;
if(l>r) break;
}
return l>r;
}
int main()
{
read(n); read(m);
for(int i=1;i<=n;i++) read(a[i]);
sort(a+1,a+n+1,cmp);
if(n<=m) printf("%d\n",a[n]);
else
{
int l=a[n],r=a[n]+a[n-1];
while(l<=r)
{
int mid=((ll)l+r)/2ll;
if(judge(mid)) r=mid-1;
else l=mid+1;
}
printf("%d\n",r+1);
}
return 0;
}
T2:dp算出1为根的答案,然后O(1)换一下根,对于n个可能的根都统计答案找最小的
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e9
using namespace std;
inline void read(int &x)
{
char c; while(!((c=getchar())>='0'&&c<='9'));
x=c-'0';
while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 410000;
int n;
int w[maxn];
struct edge{int y,c,nex;}a[maxn<<1]; int len,fir[maxn];
inline void ins(const int x,const int y,const int c){a[++len]=(edge){y,c,fir[x]};fir[x]=len;}
int fa[maxn],siz[maxn],sum[maxn];
ll f[maxn];
void dfs(const int x)
{
siz[x]=1; sum[x]=w[x];
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=fa[x])
{
fa[y]=x; dfs(y);
siz[x]+=siz[y];
sum[x]+=sum[y];
f[x]+=f[y]+(ll)sum[y]*a[k].c;
}
}
ll re,g[maxn];
ll tc[maxn],pre[maxn],suf[maxn];
int t[maxn],tp;
void cal(const int x)
{
re=min(re,g[x]+f[x]);
tp=0;
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=fa[x])
t[++tp]=y,tc[tp]=f[y]+(ll)sum[y]*a[k].c;
pre[0]=0; for(int i=1;i<=tp;i++) pre[i]=pre[i-1]+tc[i];
suf[tp+1]=0; for(int i=tp;i>=1;i--) suf[i]=suf[i+1]+tc[i];
for(int k=fir[x],y=a[k].y,i=0;k;k=a[k].nex,y=a[k].y) if(y!=fa[x])
{
++i;
g[y]=g[x]+pre[i-1]+suf[i+1]+(ll)(sum[1]-sum[y])*a[k].c;
}
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=fa[x])
cal(y);
}
int main()
{
read(n);
for(int i=1;i<n;i++)
{
int x,y,c; read(x); read(y); read(c);
ins(x,y,c); ins(y,x,c);
}
for(int i=1;i<=n;i++) read(w[i]);
dfs(1);
re=LLONG_MAX; cal(1);
printf("%lld\n",re);
return 0;
}
T3:
红绿灯之间的距离是确定的,求从家到公司的时间就是求在n个红绿灯等待的总时间
时间是在Mod C意义下的
对于第i个红绿灯,到达时如果
t∈[0,Ai)
就不需要等待,否则需要等到区间的左端点即0
我们可以将Q个询问一起做
将Q个询问的初始时间
t Mod C
塞进一个multiset里面
走到第i个红绿灯的时候,令
u=∑i−1i=0Di
,实际上第i个红绿灯的合法区间(转化成初始时间)是
[0−u,Ai−u)
把不在区间的数从multiset取出,计算等待时间,合并在一起,新建一个点代表这些合并起来的点,时间为合法区间的左端点,塞回multiset
合并和维护贡献可以写个带权并查集
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e9
using namespace std;
const int maxn = 410000;
int n,C,m;
int dis(int x,int y){return x<y?y-x:C-x+y;}
int A[maxn],D[maxn];
int fa[maxn],cnt;
ll fas[maxn];
int findfa(const int x)
{
if(fa[x]==x) return x;
int k=findfa(fa[x]);
fas[x]+=fas[fa[x]];
return fa[x]=k;
}
struct node
{
int ti,i;
friend inline bool operator <(const node x,const node y) { return x.ti<y.ti; }
};
multiset<node>S;
multiset<node>::iterator it,it2;
ll base;
int main()
{
scanf("%d%d%d",&n,&C,&m);
for(int i=1;i<=n;i++) scanf("%d",&A[i]);
for(int i=0;i<=n;i++) scanf("%d",&D[i]),base+=D[i];
for(int i=1;i<=m;i++)
{
int t; scanf("%d",&t); t%=C;
fa[++cnt]=cnt;
S.insert((node){t,i});
}
for(int i=1,ss=0;i<=n;i++)
{
ss=(ss+D[i-1])%C;
int l=0,r=A[i];
l=(l-ss+C)%C; r=(r-ss+C)%C;
if(l<r)
{
it=S.lower_bound((node){0,0});
int ok=0;
while(it!=S.end()&&(*it).ti<l)
{
it2=it; it2++;
if(!ok) ok=1,S.insert((node){l,++cnt});
node tmp=(*it); S.erase(it);
fa[tmp.i]=cnt,fas[tmp.i]=dis(tmp.ti,l);
it=it2;
}
it=S.lower_bound((node){r,0});
ok=0;
while(it!=S.end())
{
it2=it; it2++;
if(!ok) ok=1,S.insert((node){l,++cnt});
node tmp=(*it); S.erase(it);
fa[tmp.i]=cnt,fas[tmp.i]=dis(tmp.ti,l);
it=it2;
}
}
else
{
it=S.lower_bound((node){r,0});
int ok=0;
while(it!=S.end()&&(*it).ti<l)
{
it2=it; it2++;
if(!ok) ok=1,S.insert((node){l,++cnt});
node tmp=(*it); S.erase(it);
fa[tmp.i]=cnt,fas[tmp.i]=dis(tmp.ti,l);
it=it2;
}
}
}
for(int i=1;i<=m;i++)
{
findfa(i);
printf("%lld\n",base+fas[i]);
}
return 0;
}
T4:
易得dp:
f[i][j][k]表示走了i步,剩j血,饮料还有k秒的最小花费
滚动一下50分,常数比较优秀的话50分没什么问题
要拿100分需要优化这个dp,因为状态数太多了
注意到能量饮料不能叠加,贪心的想,喝了一瓶饮料后在饮料时间还比较充裕的时候肯定不会再喝一瓶
事实上可以证明这t秒内只有第t秒有可能再喝一瓶(比如在t-1秒喝,枚举t秒干什么,可以发现t-1秒喝一定不优,< t-1的时候可以类比递推)
所以在1~t-1内肯定不会喝第二瓶饮料,可以对于每个血量H,贪心处理出此时喝一瓶饮料,在接下来t-1秒行动后的血量,距离,花费
那么我们每次喝饮料,转移到饮料剩1s的状态
然后拆dp,
f[i][j][0/1]表示走了i步,j血,饮料剩余时间0/1s的最小花费
,因为饮料不能叠加直接覆盖,枚举决策转移
注意t-1s内走的步数加上原步数>=S的情况,这种情况即喝一瓶饮料后在这个持续时间内可以到达处理,再拆一个dp出来
g[i][j]表示走了i步,剩j血,持有饮料状态且饮料结束前能到达S
code:……emmmmmmmm
(被hack了就不贴了….没fix成功….我觉得做法还是没啥问题的…就是我写萎了…