• 四连测Day4


    四连爆炸

    卡我常数

    好像被AluminumGod拉到了创客...哇我这个天天爆炸的水平可能会被其他三位dalao吊起来打

    orz Edmond-Karp_XiongGod

    orz Deidara_WangGod

    orz Small_ChickenGod

    T1 树上有一些关键点,求包含i个关键点的联通块数,0<=i<=m

    n,m<=1000

    树形dp,强行证明复杂度

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    const int maxn = 1010,mod = 998244353;
    vector<int> G[maxn];
    int n,m;int a[maxn];
    int state;
    LL f[maxn][maxn],size[maxn],ans[maxn];
    inline void dp(int x,int fa)
    {
        size[x] = 1;
        if(a[x] == 1)f[x][1] = 1;
        else f[x][0] = 1;
        for(auto to : G[x])
        {
            if(to == fa)continue;
            dp(to,x);size[x] += size[to];
            for(int i=size[x];i>=0;i--)
            {
                for(int j=0;j<=i && j<=size[to];j++)
                    (f[x][i] += 1ll * f[to][j] * f[x][i - j]) %= mod;
            }
        }
        for(int i=0;i<=size[x];i++)(ans[i] += f[x][i]) %= mod;
    }
    int main()
    {
        freopen("tree.in","r",stdin);
        freopen("tree.out","w",stdout);
        n = read(),m = read();
        for(int i=1;i<=m;i++)a[read()] = 1;
        for(int i=1;i<n;i++)
        {
            int u = read(),v = read();
            G[u].push_back(v);G[v].push_back(u);
        }
        dp(1,1);
        for(int i=0;i<=m;i++)printf("%lld ",ans[i]);
    }
    View Code(虽然不是严格n^2但也卡不到n^3 比标算慢一点)
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    #define MAXN 131072
    #define MOD 998244353
    using namespace std;
    inline int read()
    {
        int x=0,t=1,c;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        while(isdigit(c))x=x*10+c-'0',c=getchar();
        return x*t;
    }
    int first[1024],nxt[2048],targ[2048],cnte=1;
    bool special[1024];
    int ans[1024];
    int dp[1024][1024],cntd[1024];
    int poly[1024];
    void AddEdge(int u,int v)
    {
        targ[cnte]=v;nxt[cnte]=first[u];first[u]=cnte++;swap(u,v);
        targ[cnte]=v;nxt[cnte]=first[u];first[u]=cnte++;
    }
    void DP(int x,int Fa)
    {
        if(special[x])
        {
            dp[x][1]=1;cntd[x]=2;
        }
        else
        {
            dp[x][0]=1;cntd[x]=1;
        }
        for(int i=first[x];i;i=nxt[i])
        {
            if(targ[i]==Fa)continue;
            int y=targ[i];
            DP(y,x);
            for(int i=0;i<cntd[x]+cntd[y]-1;i++)poly[i]=0;
            for(int i=0;i<cntd[x];i++)
                for(int j=0;j<cntd[y];j++)
                    (poly[i+j]+=(long long)dp[x][i]*dp[y][j]%MOD)%=MOD;
            cntd[x]+=cntd[y]-1;
            for(int i=0;i<cntd[x];i++)dp[x][i]=poly[i];
        }
        for(int i=0;i<cntd[x];i++)(ans[i]+=dp[x][i])%=MOD;
        dp[x][0]++;dp[x][0]%=MOD;
    }
    int main()
    {
        freopen("tree.in","r",stdin);
        freopen("tree.out","w",stdout);
        int n=read(),m=read();
        for(int i=0;i<m;i++)special[read()]=1;
        for(int i=1;i<n;i++)AddEdge(read(),read());
        DP(1,0);
        for(int i=0;i<=m;i++)printf("%d%c",ans[i],i==m?'
    ':' ');
    }
    View Code(集训队dalao的标算)

    T2

    每个机器人对每个零件有一个需求度,现在你有$n$个机器人$m$个零件,第$i$个机器人需要$ki$个零件,求所有机器人需求度和的最大值

    费用流

    S->机器人 caps = ki,cost = 0

    机器人->零件 caps = 1,cost = 需求度

    零件->T caps = 1,cost = 0

    然后跑最大费用最大流

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 210 * 110 * 4;
    long long ans;//此处ans保存费用
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    int n,m,s,t;
    int first[maxn],to[maxn],nx[maxn],caps[maxn],cost[maxn],cnt=-1;
    int vis[maxn],dis[maxn];
    deque<int> dq;
    inline void add(int u,int v,int w,int c)
    {
        to[++cnt]=v;
        nx[cnt]=first[u];
        first[u]=cnt;
        caps[cnt]=w;
        cost[cnt]=c;
    }
    inline void ins(int u,int v,int w,int c){add(u,v,w,c);add(v,u,0,-c);}
    inline int afps(int s,int t)//反向spfa 
    {
        memset(vis,0,sizeof(vis));
        memset(dis,127,sizeof(dis));
        dis[t]=0;vis[t]=1;
        dq.clear();
        dq.push_back(t);
        while(!dq.empty())
        {
            int now=dq.front();dq.pop_front();
            for(int i=first[now];i>-1;i=nx[i])
                if(caps[i^1] && dis[to[i]]>dis[now]-cost[i])
                {
                    dis[to[i]]=dis[now]-cost[i];
                    if(!vis[to[i]])
                    {
                        vis[to[i]]=1;
                        if(!dq.empty() && dis[to[i]]<dis[dq.front()])dq.push_front(to[i]);
                        else dq.push_back(to[i]);
                    }
                }
            vis[now]=0;
        }
        return dis[s]<2139062143;
    }
    inline int dfs(int u,int flow)
    {
        if(u==t){vis[t]=1;return flow;}
        int used=0,tmp;vis[u]=1;
        for(int i=first[u];i>-1;i=nx[i])
            if(!vis[to[i]] && caps[i] && dis[u]-cost[i]==dis[to[i]])
            {
                tmp=dfs(to[i],min(caps[i],flow-used));
                if(tmp)ans+=1ll*tmp*cost[i],caps[i]-=tmp,caps[i^1]+=tmp,used+=tmp;
                if(used==flow)break;
            }
        return used;
    }
    inline int zkw()
    {
        long long maxflow=0;
        while(afps(s,t))
        {
            vis[t]=1;
            while(vis[t])
            {
                memset(vis,0,sizeof(vis));
                maxflow+=(long long)dfs(s,2139062143);
            }
        } 
        return maxflow;
    }
    int main()
    {
        freopen("robot.in","r",stdin);
        freopen("robot.out","w",stdout);
        memset(nx,-1,sizeof nx);memset(first,-1,sizeof first);
        n=read(),m=read();s = 0;t = n + m + 1;
        for(int i=1;i<=n;i++)
        {
            int a = read();
            ins(s,i,a,0);
        }
        for(int i=1;i<=m;i++)ins(i + n,t,1,0);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                int a = read();
                ins(i,j + n,1,-a);
            }
        }
        zkw();
        printf("%d",-ans);
    }
    View Code

    需要注意的是这道题图很稠密,EK被卡成了暴力分

    T3

    有一维空间内$n$个点,编号从$1$到$n$,编号为$i$的点坐标为$x_i$。
    现在,请选出编号连续的一些点,使得被选出的所有点到某一点的距离和的最小值不超过一正
    整数$m$,问最多选出多少点?

    $O(nlog^2n)$的做法:(卡常的出题人嘤嘤嘤qwq)

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    inline int read()
    {
        int x = 0,f = 1;char ch = getchar();
        while(!isdigit(ch)){if(ch == '-') f = -1;ch = getchar();}
        while(isdigit(ch)) x = x * 10 + ch - '0',ch = getchar();
        return x * f;
    }
    const int maxpos = 1000000;
    int n,m;
    int res;
    int va[maxpos * 4 + 10];
    int size[maxpos * 4 + 10];
    int a[100005];
    #define ls (x << 1)
    #define rs ((x << 1) | 1)
    inline void Insert(int x,int l,int r,int val,int f)
    {
        if(l == r){size[x] += f;va[x] += (l * f);return;}
        int mid = (l + r) >> 1;
        if(val <= mid) Insert(ls,l,mid,val,f);
        else Insert(rs,mid + 1,r,val,f);
        va[x] = va[ls] + va[rs];
        size[x] = size[ls] + size[rs];
    }
    inline int kth(int x,int l,int r,int val)
    {
        if(l == r)return l;
        int mid = (l + r) >> 1;
        if(val <= size[ls]) return kth(ls,l,mid,val);
        else return kth(rs,mid + 1,r,val - size[ls]);
    }
    inline int query(int x,int l,int r,int L,int R)
    {
        if(L<= l && R >= r){res += size[x];return va[x];}
        int mid = (l + r) >> 1;
        int ret = 0;
        if(L <= mid)ret += query(ls,l,mid,L,R);
        if(R > mid)ret += query(rs,mid + 1,r,L,R);
        return ret;
    }
    inline int check(int mid)
    {
        memset(va,0,sizeof(va));memset(size,0,sizeof(size));
        for(int i = 1;i <= mid;i++)Insert(1,1,maxpos,a[i],1);
        int pos = kth(1,1,maxpos,(mid + 1) >> 1);
        res = 0;
        int ans = query(1,1,maxpos,1,pos);
        if( ( ( res * pos ) - ans ) + ( va[1] - ans) - ( (mid - res ) * pos) <= m) return 1;//!!!
        for(int i = mid + 1;i<=n;i++)
        {
            Insert(1,1,maxpos,a[i - mid], - 1);
            Insert(1,1,maxpos,a[i],1);
            pos = kth(1,1,maxpos,(mid + 1) >> 1);
            res = 0;
            ans = query(1,1,maxpos,1,pos);
            if( ( ( res * pos ) - ans ) + ( va[1] - ans) - ( (mid - res ) * pos) <= m) return 1;//!!!
        }
        return 0;
    }
    int main()
    {
        freopen("choose.in","r",stdin);
        freopen("choose.out","w",stdout);
        n = read(),m = read();
        for(int i = 1;i <= n;i++)a[i] = read();
        int l = 0,r = n,ans;
        while(l <= r)
        {
            int mid = (l + r) >> 1;
            if(check(mid)) {l = mid + 1;ans = mid;}
            else r = mid - 1;
        }
        printf("%d
    ",ans);
    }
    View Code

    $O(nlogn)$的做法:(放份好看的)

    。。。还是来写一下标算的做法吧

    可以知道到“某一点距离和的最小值”一定是这几个点的中位数

    维护一个滑动窗口,每次用平衡树查询窗口内的答案,看合不合法,处理出以每个点开头/结尾的滑窗的最长长度即可

    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    #include<vector>
    #include<set>
    #define MAXN 100100
    #define MAXX 1001000
    #define ll long long
    #define inf 2139062143
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    int n,m,l,ans,g[MAXN],val[MAXX<<2],maxn;
    ll sum[MAXX<<2];
    void mdf(int k,int l,int r,int x,int p)
    {
        if(l==r) {sum[k]+=p*x,val[k]+=p;return ;}
        int mid=(l+r)>>1;
        if(x<=mid) mdf(k<<1,l,mid,x,p);
        else mdf(k<<1|1,mid+1,r,x,p);
        sum[k]=sum[k<<1]+sum[k<<1|1],val[k]=val[k<<1]+val[k<<1|1];
    }
    ll Ask(int k,int l,int r,int x)
    {
        if(l==r) return val[k]*maxn+l;
        int mid=(l+r)>>1;
        if(val[k<<1]>=x) return Ask(k<<1,l,mid,x);
        else return Ask(k<<1|1,mid+1,r,x-val[k<<1]);
    }
    ll query(int k,int l,int r,int a,int b)
    {
        if(a>b) return 0;
        if(l==a&&r==b) return sum[k];
        int mid=(l+r)>>1;
        if(b<=mid) return query(k<<1,l,mid,a,b);
        else if(a>mid) return query(k<<1|1,mid+1,r,a,b);
        else return query(k<<1,l,mid,a,mid)+query(k<<1|1,mid+1,r,mid+1,b);
    }
    int lft(int k,int l,int r,int a,int b)
    {
        if(a>b) return 0;
        if(l==a&&r==b) return val[k];
        int mid=(l+r)>>1;
        if(b<=mid) return lft(k<<1,l,mid,a,b);
        else if(a>mid) return lft(k<<1|1,mid+1,r,a,b);
        else return lft(k<<1,l,mid,a,mid)+lft(k<<1|1,mid+1,r,mid+1,b);
    }
    ll calc(int x)
    {
        if(x==l) return 0;
        if(x-l==1) return abs(g[x]-g[l]);
        ll t=(x-l+1)/2+1,k=Ask(1,1,maxn,t),lv=lft(1,1,maxn,1,k%maxn-1),q= k/maxn+2*lv-x+l-1;k%=maxn;
        return query(1,1,maxn,k+1,maxn)-query(1,1,maxn,1,k-1)+q*k;
    }
    int main()
    {
        freopen("choose.in","r",stdin);
        freopen("choose.out","w",stdout);
        n=read(),m=read();
        for(int i=1;i<=n;i++) g[i]=read(),maxn=max(maxn,g[i]);
        for(int i=l=1;i<=n;i++)
        {
            mdf(1,1,maxn,g[i],1);
            while(calc(i)>m) {mdf(1,1,maxn,g[l],-1);l++;}
            ans=max(ans,i-l+1);
        }
        printf("%d",ans);
    }
    View Code

    出题人为了卡我的常,甚至使用了前两题开O2这题临时不开的技巧...

  • 相关阅读:
    poj2528 Mayor's posters(线段树区间修改+特殊离散化)
    codeforces 733D Kostya the Sculptor(贪心)
    codeforces 733C Epidemic in Monstropolis
    poj 2828--Buy Tickets(线段树)
    lightoj 1125
    HDU 2795 Billboard (线段树)
    hdu 5945 Fxx and game(dp+单调队列! bc#89)
    poj3666 Making the Grade(基础dp + 离散化)
    codeforces 652D
    lightoj 1140
  • 原文地址:https://www.cnblogs.com/Kong-Ruo/p/9454419.html
Copyright © 2020-2023  润新知