2018“Splay杯”GDKOI模拟赛Round 1

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=i1i=0Di ,实际上第i个红绿灯的合法区间(转化成初始时间)是 [0u,Aiu)
把不在区间的数从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]ijk
滚动一下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]ij0/1s ,因为饮料不能叠加直接覆盖,枚举决策转移
注意t-1s内走的步数加上原步数>=S的情况,这种情况即喝一瓶饮料后在这个持续时间内可以到达处理,再拆一个dp出来 g[i][j]ijS

code:……emmmmmmmm
(被hack了就不贴了….没fix成功….我觉得做法还是没啥问题的…就是我写萎了…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值