2020 杭电多校第二场

1010 Lead of Wisdom
剪枝:预处理每一行每个位置最大值,搜到第i行时如果后面几行全部取最大值(这种情况都不一定存在)的结果还是比ans小就没必要再搜了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=55;
int t,n,k,s[N][N][5],sz[N];
int suf[N][5],m[N][5];
ll ans;
void dfs(int x,int a,int b,int c,int d)
{
        if(1ll*(a+suf[x][0])*(b+suf[x][1])*(c+suf[x][2])*(d+suf[x][3])<=ans) return ;
        if(x==k+1) 
        {
                ans=max(ans,1ll*a*b*c*d);
                return ;
        }
        if(sz[x]==0) dfsLead of Wisdom(x+1,a,b,c,d);
        for(int i=0;i<sz[x];i++)
        {
                dfs(x+1,a+s[x][i][0],b+s[x][i][1],c+s[x][i][2],d+s[x][i][3]);
        }
        return ;
}
int main()
{
        scanf("%d",&t);
        while(t--)
        {
                scanf("%d%d",&n,&k);
                memset(sz,0,sizeof sz);
                memset(m,0,sizeof m);
                for(int i=1;i<=n;i++)
                {
                        int op;
                        scanf("%d",&op);
                        for(int i=0;i<4;i++)
                        {
                                scanf("%d",&s[op][sz[op]][i]);
                                m[op][i]=max(m[op][i],s[op][sz[op]][i]);
                        }
                        sz[op]++;
                }
                for(int i=0;i<4;i++) suf[k+1][i]=0;
                for(int i=k;i>0;i--)
                        for(int j=0;j<4;j++)
                            suf[i][j]=suf[i+1][j]+m[i][j];
                ans=0;
                dfs(1,100,100,100,100);
                printf("%lld\n",ans);
        }
}

再来两种玄学做法:
反向爆搜,正向会TLE ,反向就4305ms,不明白为啥会这么快

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=55;
int t,n,k,s[N][N][5],sz[N];
ll ans;
void dfs(int x,int a,int b,int c,int d)
{
        if(x==0) 
        {
                ans=max(ans,1ll*a*b*c*d);
                return ;
        }
        if(sz[x]==0) dfs(x-1,a,b,c,d);
        for(int i=0;i<sz[x];i++)
        {
                dfs(x-1,a+s[x][i][0],b+s[x][i][1],c+s[x][i][2],d+s[x][i][3]);
        }
        return ;
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
            scanf("%d%d",&n,&k);
            memset(sz,0,sizeof sz);
            for(int i=1;i<=n;i++)
            {
                    int op;
                    scanf("%d",&op);
                    for(int i=0;i<4;i++)
                            scanf("%d",&s[op][sz[op]][i]);
                    sz[op]++;
            }
            ans=0;
            dfs(k,100,100,100,100);
            printf("%lld\n",ans);
    }
}

函数random_shuffle()用来对一个元素序列进行重新排序(随机的)
随机排序之后就可以过了???还是必过…

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=55;
int t,n,k,a[N][4],c[N];
ll ans=0;
struct node
{
    int a,b,c,d;
    
};
vector<vector<node>> res;
void dfs(int x,int a,int b,int c,int d)
{
    if(x>k)
    {
        ans=max(1ll*a*b*c*d,ans);
        return ;
    }
    for(int i=0;i<res[x].size();i++)
    {
        node e=res[x][i];
        dfs(x+1,a+e.a,b+e.b,c+e.c,d+e.d);
    }
    if(res[x].size()==0) dfs(x+1,a,b,c,d);
    return ;
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
       	scanf("%d%d",&n,&k);
       	res.resize(k+3);//改变容量
        for(int i=1;i<=k;i++) res[i].clear();
        for(int i=1;i<=n;i++)
        {
            int e;
            scanf("%d",&e);
            int a,b,c,d;
            scanf("%d%d%d%d",&a,&b,&c,&d);
            res[e].push_back({a,b,c,d});
        }
        random_shuffle(res.begin()+1,res.begin()+1+k);
        ans=0;
        dfs(1,100,100,100,100);
        printf("%lld\n",ans);
    }
} 

1006 The Oculus
双哈希

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2000010;
const ll mod=1e9+7,mod1=1e9+9;
int t,a[N],b[N],c[N];
ll f[N],f1[N],sum[N];

void init()
{
    f[1]=f1[1]=1;f1[2]=f[2]=2;
    for(int i=3;i<N;i++)
    {
        f[i]=(f[i-1]+f[i-2])%mod;
        f1[i]=(f1[i-1]+f1[i-2])%mod1;
    }
    return ;
}
int main()
{
    init();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",a);
        for(int i=1;i<=a[0];i++) scanf("%d",a+i);
        scanf("%d",b);
        for(int i=1;i<=b[0];i++) scanf("%d",b+i);
        scanf("%d",c);
        for(int i=1;i<=c[0];i++) scanf("%d",c+i);
        ll res=0,res1=0,res2=0;
        for(int i=1;i<=a[0];i++)
            res1=(res1+a[i]*f[i])%mod;
        for(int i=1;i<=b[0];i++)
            res2=(res2+b[i]*f[i])%mod;
        for(int i=1;i<=c[0];i++)
            res=(res+c[i]*f[i])%mod;
        ll cnt=0,cnt1=0,cnt2=0;
        for(int i=1;i<=a[0];i++)
            cnt1=(cnt1+a[i]*f1[i])%mod1;
        for(int i=1;i<=b[0];i++)
            cnt2=(cnt2+b[i]*f1[i])%mod1;
        for(int i=1;i<=c[0];i++)
            cnt=(cnt+c[i]*f1[i])%mod1;
        for(int i=c[0];i>0;i--)
        {
            if(c[i]==0)
            {
                if((res+f[i])%mod==(res1*res2)%mod)
                {
                    printf("%d\n",i);
                    break;
                }
            }
        }
    }
    return 0;
}

1001 Total Eclipse
首先假设每个点都是独立的,sum=所有bi的和。按照bi的值从大到小排序后,遍历每个节点i,遍历与i相连的所有节点j,如果i,j还不是同一个连通块并且bi<=bj,sum-=b[i],并将i,j所在连通块合并;否则暂时不用管。已经遍历过的点一定大于等于当前点。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010,M=400010;
int t,n,m,h[N],e[M],ne[M],idx;
int pre[N],b[N];
bool st[N];
struct node
{
    int id,val;
    bool operator < (const node &w)const
    {
        return val>w.val;
     } 
}ed[N];
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a];h[a]=idx++;
}
int find(int x)
{
    return (pre[x]==x)?x:(pre[x]=find(pre[x]));
}
bool check(int a,int b)
{
    int fa=find(a);
    int fb=find(b);
    if(fa!=fb)
    {
        pre[fb]=fa;
        return 1;
    }
    else return 0;
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) h[i]=-1,pre[i]=i,st[i]=0;
        ll sum=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",b+i);
            sum+=b[i];
            ed[i]={i,b[i]};
        } 
        sort(ed+1,ed+n+1);
        idx=0;
        while(m--)
        {
            int a, b;
            scanf("%d%d",&a,&b);
            add(a,b);
            add(b,a);
        }
        for(int i=1;i<=n;i++)
        {
            int u=ed[i].id;
            for(int j=h[u];j!=-1;j=ne[j])
            {
                int v=e[j];
                if(st[v]&&check(u,v))
                {
                    sum-=ed[i].val;
                }
            }
            st[u]=1;
        }
        printf("%lld\n",sum);
    }
    return 0;
}

1012 String Distance
求区间[l,r]的a串与整个b串的lcs,先预处理a串,suf[i][j]表示a串中第i个字符后第一个字符(j+‘a’)的位置。
f[i][j]表示lcs的第i是b串的第j个字符时 这个字符在a串的第一个位置,
如果f[i][j]<=r那么满足题意继续更新下一个f[i+1][],
f[i+1][k]=min(f[i+1][k],suf[ f[i][j] ] [ b[k]-‘a’ ] ) (k>j)
否则就不符合题意了。
最终的lcs就是 满足f[i][j]<=r的最大的i。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
int t,n,m;
char a[N],b[25];
int suf[N][30];
int f[30][30];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",a+1);
        scanf("%s",b+1);
        n=strlen(a+1);
        m=strlen(b+1);
        for(int i=0;i<26;i++) suf[n][i]=n+1;
        for(int i=n-1;i>=0;i--)
        {
            for(int j=0;j<26;j++)
                suf[i][j]=suf[i+1][j];
            suf[i][a[i+1]-'a']=i+1;
        }
        int q;scanf("%d",&q);
        while(q--)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            int lcs=0;
            for(int i=1;i<=m;i++) 
                f[1][i]=suf[l-1][b[i]-'a'];
            for(int i=2;i<=m;i++)
                for(int j=1;j<=m;j++) f[i][j]=r+1;
            for(int i=1;i<=m;i++)
                for(int j=i;j<=m;j++)
                if(f[i][j]<=r)
                {
                    lcs=max(lcs,i);
                    for(int k=j+1;k<=m;k++)
                        f[i+1][k]=min(f[i+1][k],suf[f[i][j]][b[k]-'a']);
                }
            printf("%d\n",r-l+1+m-lcs*2);
        }
    }
    return 0;
}

New Equipments
每个人与设备的花费函数是单谷函数,最小值点可以三分出来或者直接用 -b/(2*a)
总共只有n个人,最多匹配n次,所以每个人只用与产生花费最小的n太设备建边就好,边的容量为1,单价费用就是产生的花费,然后再把n个人与源点建边,设备与汇点建边,边权都是1花费都是0.最后跑费用流就好。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=10010,M=100010;
const ll inf=0x3f3f3f3f3f3f3f3f;
int _,s,t,n,m,h[N],ne[M],e[M],w[M],idx;
ll a[N],b[N],c[N],cost[M];
ll ans[N];
void add(int a,int b,int c,ll d)
{
    e[idx]=b;ne[idx]=h[a];w[idx]=c;cost[idx]=d;h[a]=idx++;
    e[idx]=a;ne[idx]=h[b];w[idx]=0;cost[idx]=-d;h[b]=idx++;//反向边费用为负,能把费用退回去
} 
ll get(int i,int j)
{
    return a[i]*j*j+b[i]*j+c[i];
}
bool flag,st[N];
ll dist[N],dep[N];
int pre[N];
bool spfa()
{
    for(int i=s;i<=t;i++) dist[i]=inf,pre[i]=-1,st[i]=0;
    deque<int> q;
    q.push_back(s);
    dist[s]=0;
    st[s]=1;
    while(q.size())
    {
        int u=q.front();q.pop_front();
        st[u]=0;
        for(int i=h[u];i!=-1;i=ne[i])
        {
            int v=e[i];
            if(dist[v]>dist[u]+cost[i]&&w[i]>0)
            {
                dist[v]=dist[u]+cost[i];
                pre[v]=i;//记录最短路从那条边过来的,方便回溯
                if(!st[v])
                {
                    if(q.size()&&dist[v]<dist[q.front()]) q.push_front(v);//SLF优化
                    else q.push_back(v);
                    st[v]=1;
                }
            }
        }
    }
    return pre[t]!=-1;
}
ll mincost;
void mcmf()
{
    int k=1,flow=0;
    while(spfa())
    {
        for(int i=pre[t];i!=-1;i=pre[e[i^1]])
        {
            w[i]-=1;
            w[i^1]+=1;
            mincost+=cost[i];
        }
        ans[k++]=mincost;
        if(k>n) break;
    }
    for(int i=k;i<=n;i++) ans[i]=ans[i-1];
    return ;
}
int main()
{
    scanf("%d",&_);
    while(_--)
    {
        scanf("%d%d",&n,&m);
        memset(h,-1,sizeof h);
        for(int i=1;i<=n;i++) scanf("%lld%lld%lld",a+i,b+i,c+i);
        map<int,int> mp;
        int cnt=n+1;
        idx=0;
        for(int i=1;i<=n;i++)
        {
            int l=1,r=m;
            while(l<r)
            {
                int mid1=l+(r-l)/3;
                int mid2=r-(r-l)/3;
                if(get(i,mid1)<get(i,mid2)) r=mid2-1;
                else l=mid1+1;
            } 
            int res=n-1,pos=l;
            if(mp.count(l)==0) mp[l]=cnt++;
            add(i,mp[l],1,get(i,l));
            l++;
            while(res--)//选择总共n台设备
            {
                if(mp.count(l)==0) mp[l]=cnt++;
                add(i,mp[l],1,get(i,l));
                if(l<=pos)
                {
                    if(pos+pos-l+1<=m) l=pos*2-l+1;
                    else l--;
                }
                else 
                {
                    if(pos-l+pos>=1) l=pos-l+pos;
                    else l++;
                }
            }
        }
        t=cnt;
        for(int i=1;i<=n;i++) add(s,i,1,0);
        for(int j=n+1;j<cnt;j++) add(j,t,1,0);
        mincost=0;
        mcmf();
        for(int i=1;i<=n;i++) printf("%lld%c",ans[i],i<n?' ':'\n');
    }
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值