• 六省联考2017


    期末考试

    Luogu
    LOJ
    BZOJ
    显然我们只需要考虑最后公布成绩的那一天。
    枚举这一天,预处理前缀和之后即可(O(1))计算最小代价。
    注意倒数第二个Subtask这最优的日期就是(min(b_i)),直接计算即可。(如果套用通法会爆long long,但是开unsigned long long就行了)

    #include<cctype>
    #include<cstdio>
    #include<algorithm>
    using i64=unsigned long long;
    const int N=100007;const i64 inf=1e18;
    i64 t1[N],t2[N],A,B,C;
    int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
    i64 cal(i64 a,i64 b){return A<B? a>b? b*A+(a-b)*B:a*A:B*a;}
    int main()
    {
        int n,m;i64 r1=0,r2=0,r3=0,s1=0,s2=0,s3=0,ans=inf;
        A=read(),B=read(),scanf("%lld",&C),n=read(),m=read();
        for(int i=1,x;i<=n;++i) ++t2[x=read()],s3+=x,++r3;
        for(int i=1,x;i<=m;++i) ++t1[x=read()],s2+=x,++r2;
        for(int i=100000;i;--i)
        {
    	s1+=i*t1[i],r1+=t1[i],s2-=i*t1[i],r2-=t1[i],s3-=i*t2[i],r3-=t2[i];
    	if(!s1) continue;
    	ans=std::min(ans,cal(s1-i*r1,r2*i-s2)+(r3*i-s3)*C);
        }
        printf("%lld",ans);
    }
    

    相逢是问候

    Luogu
    LOJ
    BZOJ
    给定一个数(p),最多进行(O(log p))(pleftarrowvarphi(p))即可令(p=1)
    也就是说一个位置在被修改了(O(log p))次之后,它的值就不会变了。
    那么我们记录一个位置被修改过多少次,每次修改的时候区间内的所有位置单独拿出来暴力计算即可。
    预处理快速幂即可(O(log^2p))进行单点计算。
    为了不再访问那些被修改超过(O(log p))次之后的位置,我们可以使用线段树记录区间修改最小次数,或者利用并查集+树状数组。
    时间复杂度为(O(nlog p(log p+log n)))

    #include<cctype>
    #include<cstdio>
    #include<vector>
    #include<algorithm>
    const int N=50007,M=10007;
    int n,m,p,c,a[N],o[N],t[N],tim[N],fa[N],pw1[30][M],pw2[30][M];std::vector<int>mod;
    void inc(int&a,int b){a+=b-p,a+=a>>31&p;}
    int pow(int a,int b){return 1ll*pw1[b][a%10000]*pw2[b][a/10000]%mod[b];}
    int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
    int phi(int x){int val=x;for(int p=2;p*p<=x;++p) if(!(x%p)) for(val-=val/p;!(x%p);x/=p);return val-(x>1)*val/x;}
    int find(int x){return x==fa[x]? x:fa[x]=find(fa[x]);}
    void add(int p,int v){for(;p<=n;p+=p&-p)inc(t[p],v);}
    int ask(int p){int r=0;for(;p;p^=p&-p)inc(r,t[p]);return r;}
    void modify(int p,int t){for(a[p]=o[p]>=mod[t]?o[p]%mod[t]+mod[t]:o[p];t--;)if(a[p]=pow(a[p],t),t&&!a[p])a[p]+=mod[t];}
    void update(int l,int r){for(l=find(l);l<=r;l=find(l+1))if(add(l,p-a[l]),modify(l,++tim[l]),add(l,a[l]),tim[l]==(int)mod.size()-1)fa[l]=l+1;}
    int main()
    {
        n=read(),m=read(),p=read(),c=read(),fa[n+1]=n+1;
        for(int i=1;i<=n;++i) add(fa[i]=i,o[i]=a[i]=read());
        for(int x=p;x^1;x=phi(x)) mod.push_back(x);
        mod.push_back(1),mod.push_back(1);
        for(int i=0;i<(int)mod.size();++i)
        {
    	for(int j=pw1[i][0]=1;j<=10000;++j) pw1[i][j]=1ll*pw1[i][j-1]*c%mod[i];
    	for(int j=pw2[i][0]=1;j<=10000;++j) pw2[i][j]=1ll*pw2[i][j-1]*pw1[i][10000]%mod[i];
        }
        for(int o,l,r;m;--m) o=read(),l=read(),r=read(),o? printf("%d
    ",(ask(r)-ask(l-1)+p)%p):(update(l,r),0);
    }
    

    组合数问题

    Luogu
    LOJ
    BZOJ
    (sumlimits_{iequiv rpmod k}{nkchoose i}=[x^r](1+x)^{nk}mod(x^k-1))
    循环卷积快速幂即可。

    #include<cstdio>
    #include<vector>
    using vec=std::vector<int>;
    int n,r,p,k;
    void inc(int&a,int b){a+=b-p,a+=a>>31&p;}
    int mul(int a,int b){return 1ll*a*b%p;}
    vec operator*(const vec&f,const vec&g)
    {
        vec h(k);
        for(int i=0;i<k;++i) for(int j=0;j<k;++j) inc(h[(i+j)%k],mul(f[i],g[j]));
        return h;
    }
    int main()
    {
        scanf("%d%d%d%d",&n,&p,&k,&r);vec E(k),I(k);
        I[0]=1,k==1? E[0]=2%p:E[0]=E[1]=1;
        for(long long e=1ll*n*k;e;e>>=1,E=E*E) if(e&1) I=E*I;
        printf("%d",I[r]);
    }
    

    摧毁树状图

    Luogu
    LOJ
    BZOJ
    很显然是一个树形dp。
    (f_{u,0})表示(u)子树内过(u)的一条单链。
    (f_{u,1})表示(u)子树内不经过(u)的一条路径。
    (f_{u,2})表示(u)子树内过(u)的一条路径。
    (f_{u,3})表示(u)子树内的一条路径和过(u)的一条单链。
    具体的看这个吧Link
    或者写个暴力拍,拍出错就加一种情况。
    考场上这种旷野大讨论的题一般都不会去做的。

    #include<cctype>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    const int N=100007;
    int ans,f[N][4],deg[N];std::vector<int>e[N];char ibuf[1<<23|1],*iS=ibuf;
    int read(){int x=0;while(isspace(*iS))++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return x;}
    void max(int&a,int b){if(b>a)a=b;}
    void dfs(int u,int fa)
    {
        f[u][0]=f[u][2]=f[u][3]=deg[u],f[u][1]=1;int mx=0;
        for(int v:e[u])
        {
    	if(v==fa) continue;
    	dfs(v,u);
    	max(ans,f[u][3]+f[v][0]-(u==1));
            max(ans,f[u][0]+f[v][3]-(u==1));
            max(ans,f[u][1]+f[v][2]);
            max(ans,f[u][1]+f[v][1]-1);
            max(ans,f[u][2]+f[v][1]-(u==1));
            max(ans,f[u][2]+f[v][2]-(u==1));
            max(f[u][1],f[v][1]);
            max(f[u][1],f[v][2]+1);
            max(f[u][3],f[u][0]+f[v][2]-1);
            max(f[u][3],f[u][2]+f[v][0]-1);
            max(f[u][3],f[v][3]+deg[u]-1);
            max(f[u][3],f[v][0]+mx+deg[u]-2);
            max(f[u][3],f[u][0]+f[v][1]-1);
            max(f[u][2],f[u][0]+f[v][0]-1);
            max(f[u][0],f[v][0]+deg[u]-1);
            max(f[u][2],f[u][0]);
            max(f[u][3],f[u][2]);
            max(mx,f[v][1]);
            max(mx,f[v][2]);
        }
    }
    int main()
    {
        fread(ibuf,1,1<<23,stdin);
        for(int T=read(),o=read(),n;T;--T)
        {
    	n=read(),memset(deg+1,0,n<<2);
    	for(int i=1;i<=n;++i) e[i].clear();
    	for(int i=1;i<=o;++i) read(),read();
    	for(int i=2,u,v;i<=n;++i) ++deg[u=read()],++deg[v=read()],--deg[i],e[u].push_back(v),e[v].push_back(u);
    	dfs(1,ans=0),printf("%d
    ",ans);
        }
    }
    

    分手是祝愿

    Luogu
    LOJ
    BZOJ
    显然最优的决策是倒序遍历,碰到开着的灯就关上。
    然后我们可以枚举约数变枚举倍数在(O(nlog n))的时间复杂度内求出最小操作次数(cnt)
    (f_i)表示从最小操作次数为(i)的状态转移到最小操作次数为(i-1)的期望操作次数。
    转移方程很显然,(f_i=frac in+frac{n-i}n(f_i+f_{i+1}+1))(f_i=frac{n+(n-i)f_{i+1}}i)
    最后答案就是(min(k,cnt)+sumlimits_{i=k+1}^{cnt}f_i)

    #include<cstdio>
    #include<algorithm>
    const int P=100003;
    int a[P],f[P];
    int read(){int x;scanf("%d",&x);return x;}
    void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
    int mul(int a,int b){return 1ll*a*b%P;}
    int pow(int a,int b){int r=1;for(;b;b>>=1,a=mul(a,a))if(b&1)r=mul(a,r);return r;}
    int main()
    {
        int n=read(),k=read(),cnt=0,ans;f[n+1]=0;
        for(int i=1;i<=n;++i) a[i]=read();
        for(int i=n;i;cnt+=a[i--]) for(int j=2*i;j<=n;j+=i) a[i]^=a[j];
        for(int i=n;i;--i) f[i]=mul(mul(n-i,f[i+1])+n,pow(i,P-2));
        ans=std::min(cnt,k);
        for(int i=cnt;i>k;--i) inc(ans,f[i]);
        for(int i=1;i<=n;++i) ans=mul(ans,i);
        printf("%d",ans);
    }
    

    寿司餐厅

    Luogu
    LOJ
    BZOJ
    首先价格里面的(cx)部分可以直接由(d_{i,i}leftarrow d_{i,i}-a_i)完成。
    然后我们发现这就是个标准的最大权闭合子图,选(d_{i,j})必须选(d_{i,j-1},d_{i+1,j})
    然后考虑价格中(mx^2)这部分,建(max(a))个点,选(d_{i,i})必须选(-a_i)即可。

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    const int N=107,V=6507,E=60007,inf=1e9;
    int a[N],id[N][N],d[N][N],s,t,tot=1,head[V],cur[V],dep[V];struct edge{int v,f,next;}e[E];std::queue<int>q;
    int read(){int x;scanf("%d",&x);return x;}
    void add(int u,int v,int f){e[++tot]={v,f,head[u]},head[u]=tot,e[++tot]={u,0,head[v]},head[v]=tot;}
    int bfs()
    {
        memset(dep+1,0x3f,t<<2),memcpy(cur+1,head+1,t<<2),dep[s]=0,q.push(s);
        for(int i,u,v;!q.empty();) for(u=q.front(),q.pop(),i=head[u];i;i=e[i].next) if(dep[v=e[i].v]>inf&&e[i].f) dep[v]=dep[u]+1,q.push(v);
        return dep[t]<inf;
    }
    int dfs(int u,int lim)
    {
        if(!lim||u==t) return lim;
        int v,flow=0;
        for(int&i=cur[u],f;i;i=e[i].next)
            if(dep[v=e[i].v]==dep[u]+1&&(f=dfs(v,std::min(lim,e[i].f))))
            {
                flow+=f,lim-=f,e[i].f-=f,e[i^1].f+=f;
                if(!lim) break;
            }
        return flow;
    }
    int main()
    {
        int n=read(),m=read(),ans=0,cnt=0;s=n*(n+1)/2+1001,t=s+1;
        for(int i=1;i<=n;++i) a[i]=read();
        for(int i=1;i<=n;++i) for(int j=i;j<=n;++j) d[i][j]=read()-(i==j)*a[i],id[i][j]=++cnt;
        for(int i=1;i<=n;++i) for(int j=i;j<=n;++j) if(i^j) add(id[i][j],id[i+1][j],inf),add(id[i][j],id[i][j-1],inf); else if(m) add(id[i][j],cnt+a[i],inf);
        for(int i=1;i<=n;++i) for(int j=i;j<=n;++j) d[i][j]>0? ans+=d[i][j],add(s,id[i][j],d[i][j]):add(id[i][j],t,-d[i][j]);
        if(m) for(int i=1;i<=1000;++i) add(cnt+i,t,i*i);
        while(bfs()) ans-=dfs(s,inf);
        printf("%d",ans);
    }
    
  • 相关阅读:
    物联网操作系统HelloX开发者入门指南
    【 D3.js 高级系列 】 总结
    【 D3.js 高级系列 — 10.0 】 思维导图
    android fragment+ FragmentTabHost+viewpager 切换状态不保存的问题
    OpenGL 顶点缓存对象
    OpenGL顶点数组
    【 D3.js 高级系列 — 9.0 】 交互式提示框
    如何在 Linux 上录制你的终端操作
    程序员诗词大赛开始了_你看过吗?
    程序员与代码的搞笑日常
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12733896.html
Copyright © 2020-2023  润新知