2017-3-23校内训练

学长日常出丧题虐人 100+90+64=254/300

T1.数据结构

题目大意:给定n个数,每次给出x和y,询问一个区间内多少数模x等y。(n,询问次数,数字大小<=40000)

思路:用差分或者说是前缀和的思想,离线处理每个询问,我们从左到右把数字加入某种能查询模x等y的数据结构中,碰到询问区间左端点时让该询问的答案减去当前数据结构中模x等y的个数,碰到右端点时再加上这个个数。那么如何维护这个数据结构呢?可以分类讨论,对于x<=200,我们用数组f1[i][j]存下模i等j的值的个数,每次O(200)暴力修改,O(1)查询,对于x>200,我们用f2[i]存下值为i的个数,每次O(1)修改,查询的时候我们枚举y,x+y,2x+y……复杂度最大为O(40000/200=200)。总复杂度O(200(n+q))。

#include<cstdio>
#include<algorithm>
using namespace std;
inline int read()
{
    int x;char c;
    while((c=getchar())<'0'||c>'9');
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
    return x;
}
#define MN 40000
#define K 200
struct query{int p,x,y,i,f;}p[MN*2+5];
bool cmp(query a,query b){return a.p==b.p?a.f<b.f:a.p<b.p;}
int a[MN+5],ans[MN+5],f[K+5][K+5],ff[MN+5];
int cal(int x,int y)
{
    if(x<=K)return f[x][y];
    int i,r=0;
    for(i=y;i<=MN;i+=x)r+=ff[i];
    return r;
}
int main()
{
    freopen("datastructure.in","r",stdin);
    freopen("datastructure.out","w",stdout);
    int n,q,i,j,k,l,r,x,y;
    n=read();q=read();
    for(i=1;i<=n;++i)a[i]=read();
    for(i=0;i<q;++i)
    {
        l=read();r=read();x=read();y=read();
        p[i<<1]=(query){l,x,y,i,-1};
        p[i<<1|1]=(query){r,x,y,i,1};
    }
    sort(p,p+(q<<1),cmp);
    for(i=1,j=0;i<=n;++i)
    {
        for(;p[j].p==i&&p[j].f<0;++j)ans[p[j].i]-=cal(p[j].x,p[j].y);
        for(k=1;k<=K;++k)++f[k][a[i]%k];++ff[a[i]];
        for(;p[j].p==i;++j)ans[p[j].i]+=cal(p[j].x,p[j].y);
    }
    for(i=0;i<q;++i)printf("%d
",ans[i]);
    fclose(stdin);fclose(stdout);return 0;
}

T2.刷水

题目大意:n道题,第i道在xi时出现,每道题开始难度为0,之后每秒增加wi,总共可以做k次题,问做完所有题的最小难度和。(n,k<=5000)

思路:容易想到用f[i][j]表示做i次做完前j道的最小难度和,则f[i][j]=min(f[i-1][p]+Σwl*(xj-xl)) (p<l<j),随便斜率优化就可以了,复杂度O(nk),我突然脑抽没想出来,打了决策单调性,多个log,本机测极限数据2.3s,时限3s,就放心交了,结果因为迷之原因被卡常了两个点(评测机配置与本机相同),拿到包自己测了下A了,玄学。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
inline int read()
{
    int x;char c;
    while((c=getchar())<'0'||c>'9');
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
    return x;
}
#define MN 5000
#define INF (1LL<<60)
ll f[MN+5][MN+5];
int x[MN+5],w[MN+5],q[MN+5],ql,qr,t[MN+5];
int cal(int k,int n,int x,int y)
{
    int l=x+1,r=n,mid,res=n+1;
    while(l<=r)
    {
        mid=l+r>>1;
        if(f[k][x]+f[mid][x]>f[k][y]+f[mid][y])l=mid+1;
        else res=mid,r=mid-1;
    }
    return res;
}
int main()
{
    freopen("easyproblem.in","r",stdin);
    freopen("easyproblem.out","w",stdout);
    int n,k,i,j,l;
    n=read();k=read();
    if(k>=n)return 0*puts("0");
    for(i=1;i<=n;++i)x[i]=read(),w[i]=read();
    for(i=1;i<=n;++i)for(j=i;j--;)
        f[i][j]=f[i][j+1]+1LL*w[j+1]*(x[i]-x[j+1]);
    for(i=1;i<=n;++i)f[0][i]=INF;
    if(n<=300)
        for(l=1;l<=k;++l)for(i=l;i<=n;++i)for(f[l][i]=INF,j=l-1;j<i;++j)
            f[l][i]=min(f[l][i],f[l-1][j]+f[i][j]);
    else
        for(l=1;l<=k;++l)for(ql=0,qr=-1,i=l;i<=n;++i)
        {
            while(ql<qr&&(t[qr]=cal(l-1,n,i-1,q[qr]))<=t[qr-1])--qr;
            if(ql==qr)t[qr]=cal(l-1,n,i-1,q[qr]);q[++qr]=i-1;
            while(ql<qr&&t[ql]<=i)++ql;
            f[l][i]=f[l-1][q[ql]]+f[i][q[ql]];
        }
    cout<<f[k][n];
    fclose(stdin);fclose(stdout);return 0;
}

T3.你的三角形已如风中残烛

题目大意:题名很劲爆,题意是这样的:给定一张n个点的无向图,选出若干个点,使得由这些点组成的三角形个数与选的点个数比值最大,输出方案。(n<=50)

思路:我写的小数据暴搜,大数据自己构造几个比较优而且好找的解取最优输出,得分64。

#include<cstdio>
#include<cstdlib>
inline int read()
{
    int x;char c;
    while((c=getchar())<'0'||c>'9');
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
    return x;
}
#define MN 50
int n,g[MN+5][MN+5],a[MN+5],an,u[MN+5];
int px[MN*MN+5],py[MN*MN+5],cnt;
double ans;
void dfs(int x,int k,int s)
{
    int i;
    if(x>n)
    {
        if((k?(double)s/k:0)>=ans)
        {
            ans=k?(double)s/k:0;
            for(an=0,i=1;i<=n;++i)if(u[i])a[++an]=i;
        }
        return;
    }
    u[x]=0;dfs(x+1,k,s);
    for(i=1;i<=cnt;++i)if(g[x][px[i]]&&g[x][py[i]])++s;
    for(i=1;i<x;++i)if(u[i]&&g[x][i])px[++cnt]=i,py[cnt]=x;
    u[x]=1;dfs(x+1,k+1,s);
    for(i=1;i<x;++i)if(u[i]&&g[x][i])--cnt;
}
void cal()
{
    int i,j,s=0,k=0;
    for(cnt=0,i=1;i<=n;++i)if(u[i])
    {
        ++k;
        for(j=1;j<=cnt;++j)if(g[i][px[j]]&&g[i][py[j]])++s;
        for(j=1;j<i;++j)if(u[j]&&g[i][j])px[++cnt]=i,py[cnt]=j;
    }
    if((k?(double)s/k:0)>ans)
    {
        ans=k?(double)s/k:0;an=0;
        for(i=1;i<=n;++i)if(u[i])a[++an]=i;
    }
}
void sb()
{
    int i,j,k,l;
    for(i=1;i<=n;++i)u[i]=1;cal();
    for(i=1;i<=n;++i)u[i]=0,cal(),u[i]=1;
    for(i=1;i<=1000;++i,cal())for(j=1;j<=n;++j)u[j]=rand()&1;
    for(i=1;i<=n;++i)for(j=i+1;j<=n;++j)for(k=j+1;k<=n;++k)
        if(g[i][j]&&g[j][k]&&g[k][i]){for(l=1;l<=n;++l)u[l]=0;u[i]=u[j]=u[k]=1;cal();}
    for(i=1;i<=n;++i)for(j=i+1;j<=n;++j)for(k=j+1;k<=n;++k)for(l=k+1;l<=n;++l)
        if(g[i][j]&&g[i][k]&&g[i][l]&&g[j][k]&&g[j][l]&&g[k][l])
            {for(int p=1;p<=n;++p)u[p]=0;u[i]=u[j]=u[k]=u[l]=1;cal();}
}
int main()
{
    srand(23333);
    freopen("triangle.in","r",stdin);
    freopen("triangle.out","w",stdout);
    int i,j;
    n=read();
    for(i=1;i<=n;++i)for(j=1;j<=n;++j)g[i][j]=read();
    if(n<=20)dfs(1,0,0);
    else sb();
    printf("%d
",an);
    for(i=1;i<=an;++i)printf("%d ",a[i]);
    fclose(stdin);fclose(stdout);return 0;
}
View Code

正解:二分答案后最大流,每个三角形建一个点,向T连1,组成该三角形的三个点向它连INF,每次S向点连二分出的答案,则若三角形连向T的边均满流则当前二分答案偏大,否则偏小。二分出答案后删掉所有从S开始只走还有流量的边能到的点,剩下的点即为方案。复杂度O(MaxFlow(n^3,n^3))。

#include<cstdio>
#include<cmath>
#include<cstring>
inline int read()
{
    int x;char c;
    while((c=getchar())<'0'||c>'9');
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
    return x;
}
#define MN 50
#define MV 19650
#define ME 78450
#define S MV+1
#define T MV+2
#define eps 1e-6
#define INF 1e6
struct edge{int nx,t;double w;}e[ME*2+5];
int g[MN+5][MN+5],h[MV+5],en=1,q[MV+5],qn,d[MV+5],c[MV+5];
inline void ins(int x,int y,double w)
{
    e[++en]=(edge){h[x],y,w};h[x]=en;
    e[++en]=(edge){h[y],x,0};h[y]=en;
}
bool bfs()
{
    int i,j;
    memset(d,0,sizeof(d));
    for(d[q[i=qn=0]=S]=1;i<=qn;++i)for(j=c[q[i]]=h[q[i]];j;j=e[j].nx)
        if(e[j].w>eps&&!d[e[j].t])d[q[++qn]=e[j].t]=d[q[i]]+1;
    return d[T];
}
double dfs(int x,double r)
{
    if(x==T)return r;
    double k,u=0;
    for(int&i=c[x];i;i=e[i].nx)if(e[i].w>eps&&d[e[i].t]==d[x]+1)
    {
        k=dfs(e[i].t,r-u<e[i].w?r-u:e[i].w);
        u+=k;e[i].w-=k;e[i^1].w+=k;
        if(fabs(u-r)<eps)return u;
    }
    return d[x]=0,u;
}
void dfs(int x)
{
    d[x]=1;
    for(int i=h[x];i;i=e[i].nx)if(!d[e[i].t]&&e[i].w>eps)dfs(e[i].t);
}
int main()
{
    int n=read(),i,j,k,cnt=n;double l,r,mid,ans;
    for(i=1;i<=n;++i)ins(S,i,0);
    for(i=1;i<=n;++i)for(j=1;j<=n;++j)g[i][j]=read();
    for(i=1;i<=n;++i)for(j=1;j<i;++j)for(k=1;k<j;++k)
        if(g[i][j]&&g[j][k]&&g[k][i])ins(++cnt,T,1),ins(i,cnt,1),ins(j,cnt,1),ins(k,cnt,1);
    for(l=0,r=400,k=0;k<=50;++k)
    {
        mid=(l+r)/2;
        for(i=h[S];i;i=e[i].nx)e[i].w=mid,e[i^1].w=0;
        memset(d,0,sizeof(d));
        for(d[q[i=qn=0]=S]=1;i<=qn;++i)for(j=h[q[i]];j;j=e[j].nx)
        {
            if(!d[e[j].t])d[q[++qn]=e[j].t]=d[q[i]]+1;
            if(d[e[j].t]>d[q[i]])e[j].w+=e[j^1].w,e[j^1].w=0;
        }
        for(ans=0;bfs();)ans+=dfs(S,INF);
        for(i=h[T];i;i=e[i].nx)if(e[i].w>eps&&e[i].w<1-eps)break;
        (i?l:r)=mid;
    }
    memset(d,cnt=0,sizeof(d));dfs(S);
    for(i=1;i<=n;++i)if(!d[i])++cnt;
    printf("%d
",cnt);
    for(i=1;i<=n;++i)if(!d[i])printf("%d ",i);
}
原文地址:https://www.cnblogs.com/ditoly/p/20170323C.html