四连测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?'\n':' ');
}
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\n",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这题临时不开的技巧...

posted @ 2018-08-10 13:07  探险家Mr.H  阅读(179)  评论(3编辑  收藏  举报