• Gym101002 2016NAIPC(队内第7次训练)


    (由于先看的最后一题,然后又一直WA,导致这场有点爆炸,我背锅。

    A .Fancy Antiques

    题意: 选择最多k个商店,买n个物品,每个物品分别对应两个店售卖,求最小花费是多少。n<100,k=m<=40;

    思路:搜索。。。。开始以为是个费用流,然后没法限制。加N多减枝,然后....

     

    B. 好像没什么可以学的,懒得看了

     

    C .Greetings!

    pro:N种卡片,用K种格子去装。问浪费的空间。N,K<15

    sol:比较小,状态压缩。

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    int n,k,w[20],h[20],q[20];
    long long f[1<<16][20];
    int main(){
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)scanf("%d%d%d",w+i,h+i,q+i);
        memset(f,127,sizeof(f));
        for(int i=0;i<=k;i++)f[0][i]=0;
        for(int i=1;i<(1<<n);i++)
            for(int p=i;p;p=(p-1)&i){
                int maxw=0,maxh=0;long long now=0;
                for(int j=1;j<=n;j++)
                    if(1<<(j-1)&p)maxw=max(maxw,w[j]),maxh=max(maxh,h[j]);
                for(int j=1;j<=n;j++)
                    if(1<<(j-1)&p)now+=(long long)(maxw*maxh-w[j]*h[j])*q[j];
                for(int j=1;j<=k;j++)
                    f[i][j]=min(f[i][j],f[i^p][j-1]+now);
            }
        printf("%lld",f[(1<<n)-1][k]);
        return 0;
    }
    View Code

    D .Programming Team

    题意:给定一棵大小为N的点权树(si,pi),现在让你选敲好K个点,需要满足如果如果u被选了,那么fa[u]一定被选,现在要求他们的平均值(pi之和/si之和)最大。

    思路:均值最大,显然需要01分数规划,但是后面怎么高效的做背包,我是不会的,我只会暴力的背包,O(N*K*K)。 还好队友会,叫“树上依赖背包”,即要问树转化为DFS序,按照倒序来DP,复杂度O(N*K)。dp[i][j]=max(dp[i+1][j-v[x]]+w[x],dp[i+sz[x]][j]),分别对应x选或者不选(其中x是DFS序为i的点)。

    #include<bits/stdc++.h>
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=2610;
    const double inf=1e20;
    double dp[maxn][maxn],s[maxn],p[maxn],val[maxn];
    int r[maxn],Laxt[maxn],Next[maxn],To[maxn];
    int pos[maxn],tot,cnt,N,K,sz[maxn];
    void add(int u,int v)
    {
        Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v;
    }
    void dfs(int u)
    {
        sz[u]=1; tot++; pos[tot]=u;
        for(int i=Laxt[u];i;i=Next[i]){
            dfs(To[i]); sz[u]+=sz[To[i]];
        }
    }
    bool check(double Mid)
    {
        rep(i,1,N) val[i]=p[i]-s[i]*Mid;
        rep(i,1,tot+1) rep(j,1,K) dp[i][j]=-inf;
        rep(i,0,tot+1) dp[i][0]=0;
        for(int i=tot;i>=1;i--){
            int x=pos[i];
            for(int j=1;j<=K;j++){
                dp[i][j]=max(dp[i+1][j-1]+val[x],dp[i+sz[x]][j]);
            }
        }
        return dp[1][K]>=0;
    }
    int main()
    {
        scanf("%d%d",&K,&N); K++;
        rep(i,1,N){
            scanf("%lf%lf%d",&s[i],&p[i],&r[i]);
            add(r[i],i);
        }
        dfs(0);
        double L=0,R=10000,Mid,ans=0; int T=30;
        while(T--){
            Mid=(L+R)/2;
            if(check(Mid)) ans=max(Mid,ans),L=Mid;
            else R=Mid;
        }
        printf("%.3lf
    ",ans);
        return 0;
    }
    View Code

    E. K-Inversions

    pro:对(A,B)的长度贡献。

    sol:FFT模板题。

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    struct cp{
        double x,y;
        cp operator +(const cp&a)const{
            return (cp){x+a.x,y+a.y};
        }
        cp operator -(const cp&a)const{
            return (cp){x-a.x,y-a.y};
        }
        cp operator *(const cp&a)const{
            return (cp){x*a.x-y*a.y,x*a.y+y*a.x};
        }
    }a[5000000],b[5000000],c[5000000];
    int n,m,len,ans[5000000];char s[1000010];
    double pi=3.14159265358979323846;
    void DFT(cp *a,int len,int type){
        for(int i=0,t=0;i<len;i++){
            if(i<t)std::swap(a[i],a[t]);
            for(int j=len>>1;(t^=j)<j;j>>=1);
        }
        for(int h=2;h<=len;h<<=1){
            cp wn=(cp){cos(pi*2*type/h),sin(pi*2*type/h)};
            for(int i=0;i<len;i+=h){
                cp w=(cp){1,0},t;
                for(int j=0;j<h>>1;j++,w=w*wn){
                    t=a[i+j+(h>>1)]*w;
                    a[i+j+(h>>1)]=a[i+j]-t;
                    a[i+j]=a[i+j]+t;
                }
            }
        }
    }
    int main(){
        scanf("%s",s);n=strlen(s);
        for(int i=0;i<n;i++)
            if(s[i]=='A')a[i].x=1;
            else b[n-i-1].x=1;
        for(len=1;len<=n+n;len<<=1);
        DFT(a,len,1),DFT(b,len,1);
        for(int i=0;i<len;i++)c[i]=a[i]*b[i];
        DFT(c,len,-1);
        for(int i=0;i<len;i++)ans[i]=c[i].x/len+0.1;
        for(int i=n;i<n+n-1;i++)printf("%d
    ",ans[i]);
        return 0;
    }
    View Code

    G .Symmetry

    pro:现在给你二维平面上N个点,现在让你求,加最少的点,使得所有点关于同一个点或者线有对称点。N<1e3;

    sol:好像是枚举的,枚举对称中心,枚举对称轴,复杂度O(N^3);

    H .Jewel Thief

    pro:给定N,M。输入N个物品,(si,vi)表示第i个物品体积为si,价值为vi,s<=300,vi<=1e9; N<1e6;现在要求,对于背包体积为1到M时,求出最大背包价值。

    sol:显然直接跑背包会爆炸。 发现物品体积都比较小,我们先对相同体积的排序,对于体积相同的一起处理。

    然后发现转移都是在差为体积整数倍之间,按照容量对体积取模分组 ,发现组内部转移满足神奇的决策单调性。 然后就是s次分治。

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    #define rep2(i,a,b) for(int i=b;i>=a;i--)
    using namespace std;
    const int maxn=310;
    const int maxm=100010;
    ll dp[2][maxm];
    vector<ll>G[maxn]; int x,d,t;
    void get(int L,int R,int l,int r)
    {
        if(L>R) return ;
        int Mid=(L+R)>>1,pos=Mid;
        for(int i=min(r,Mid-1);i>=l;i--){
            if(Mid-i>G[x].size()) break;
            if(dp[t][d+Mid*x]<dp[t^1][d+i*x]+G[x][Mid-i-1]){
                pos=i; dp[t][d+Mid*x]=dp[t^1][d+i*x]+G[x][Mid-i-1];
            }
        }
        get(L,Mid-1,l,pos);
        get(Mid+1,R,pos,r);
    }
    int main()
    {
        int N,M,s;ll v;
        scanf("%d%d",&N,&M);
        rep(i,1,N){
            scanf("%d%lld",&s,&v);
            G[s].push_back(v);
        }
        rep(i,1,300){
            if(G[i].size()==0) continue;
            sort(G[i].begin(),G[i].end(),greater<int>() );
            for(int j=1;j<G[i].size();j++) G[i][j]+=G[i][j-1];
            t^=1;  rep(j,1,M) dp[t][j]=dp[t^1][j];
            rep(j,0,i-1){
                d=j;  x=i;
                get(0,(M-j)/i,0,(M-j)/i);
            }
            rep(j,1,M) dp[t][j]=max(dp[t][j],dp[t][j-1]);
        }
        rep(i,1,M) printf("%lld ",dp[t][i]);
        return 0;
    }
    View Code

    I. Tourists

    pro:给定一棵树,求所有倍数关系的点之间距离之和。

    sol:枚举所有的,倍增求LCA,O(N*logN*logN),也可以用ST表O(NlogN);

    #include<bits/stdc++.h>
    #define pii pair<int,int>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    struct edge{
        int d,nex;
    }e[400010];
    int n,q[200010],l,efree,d[200010],f[20][200010];
    long long ans;
    inline void add(int x,int y){e[++efree]=(edge){y,q[x]};q[x]=efree;}
    void dfs(int x,int y){
        f[0][x]=y;d[x]=d[y]+1;
        for(int i=q[x];i;i=e[i].nex)
            if(e[i].d!=y)dfs(e[i].d,x);
    }
    inline int lca(int x,int y){
        if(d[x]>d[y])swap(x,y);
        for(int i=log2(d[y]-d[x]);i>=0;i--)
            if(d[f[i][y]]>=d[x])y=f[i][y];
        if(x==y)return x;
        for(int i=log2(d[x]);i>=0;i--)
            if(f[i][x]!=f[i][y])x=f[i][x],y=f[i][y];
        return f[0][x];
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<n;i++){
            int x,y;scanf("%d%d",&x,&y);
            add(x,y),add(y,x);
        }
        dfs(1,0);l=log2(n);
        for(int i=1;i<=l;i++)
            for(int j=1;j<=n;j++)f[i][j]=f[i-1][f[i-1][j]];
        for(int i=1;i<=n>>1;i++)
            for(int j=i+i;j<=n;j+=i){
                int fa=lca(i,j);
                ans+=d[i]+d[j]-2*d[fa]+1;
            }
        printf("%lld",ans);
        return 0;
    }
    View Code

    F. Mountain Scenes

    pro:给定总长度为N的木棍,一个W*H的框框,问有多少种分放方式,使得图案不是平的。N<10000;W,H<100

    sol:DP即可,用总的分放方案减去平的。

    #include<bits/stdc++.h>
    #define pii pair<int,int>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    ll dp[101][10001];
    const int MOD = 1e9 + 7;
    int main()
    {
        int n, w, h;
        scanf("%d%d%d", &n, &w, &h);
        if(n > w * h)n = w * h;
        for(int i = 0; i <= w; i++)dp[i][0] = 1;
        for(int i = 1; i <= w; i++)
            for(int j = 1; j <= n && j <= i * h; j++)
                for(int k = 0; k <= h && k <= j; k++)dp[i][j] += dp[i - 1][j - k], dp[i][j] %= MOD;
        ll ans = 0;
        for(int i = 1; i <= n; i++)ans += dp[w][i], ans %= MOD;
        ans -= (n / w);
        printf("%lld
    ", ans);
        return 0;
    }
    View Code

    J .Whiteboard

    pro:给定N*M的画板,然后给定画板的最终状态,以及画笔的走向。现在在某个时间T,画笔变为了橡皮擦,即最开始画板的白色的,T之前笔走过是染黑,T之后笔走过的漂白,求T的范围[L,R],不存在则输出-1 -1。

    sol:首先,如果画笔没有走过的地方是黑色'#',则无解。

             对于画笔走过的地方,我们关系的其实只有最后一次走过的时间ti,对于黑色,L>=ti; 对于白色,R<ti;

    这种题,我们倒着去删点就可以了,删点可以利用并查集,链表,或者直接用set的二分功能即可。

    注意使用long long。

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=1000010;
    char c[maxn],s[maxn],q[maxn][8];
    set<int>RR[maxn],CC[maxn];
    set<int>::iterator it;
    int d[maxn],X[maxn],Y[maxn],N,M,Q;
    ll times[maxn],Mx[maxn];
    void solve(int nowx,int nowy,int tox,int toy,int x,int y,ll nowt)
    {
        if(abs(x)==1){
            int L=min(nowx,tox),R=max(nowx,tox);
            for(it=CC[nowy].lower_bound(L);;it=CC[nowy].lower_bound(L)){
                if(it==CC[nowy].end()||(*it)>R) break;
                L=*it;CC[nowy].erase(it);
                RR[L].erase(RR[L].find(nowy));
                Mx[(L-1)*M+nowy]=nowt+abs(L-nowx);
            }
        }
        else {
            int L=min(nowy,toy),R=max(nowy,toy);
            for(it=RR[nowx].lower_bound(L);;it=RR[nowx].lower_bound(L)){
                if(it==RR[nowx].end()||(*it)>R) break;
                L=*it;RR[nowx].erase(it);
                CC[L].erase(CC[L].find(nowx));
                Mx[(nowx-1)*M+L]=nowt+abs(L-nowy);
            }
        }
    }
    int main()
    {
        scanf("%d%d%d",&N,&M,&Q);
        for(int i=N;i>=1;i--) {
            scanf("%s",c+1);
            rep(j,1,M) s[(i-1)*M+j]=c[j];
        }
        rep(i,1,Q) scanf("%s%d",q[i],&d[i]);
        rep(i,1,N)
          rep(j,1,M) RR[i].insert(j),CC[j].insert(i);
        X[0]=1; Y[0]=1; times[0]=1;
        rep(i,1,Q){
            if(q[i][0]=='u') X[i]=X[i-1]+d[i],Y[i]=Y[i-1];
            else if(q[i][0]=='d') X[i]=X[i-1]-d[i],Y[i]=Y[i-1];
            else if(q[i][0]=='r') X[i]=X[i-1],Y[i]=Y[i-1]+d[i];
            else X[i]=X[i-1],Y[i]=Y[i-1]-d[i];
            times[i]=times[i-1]+d[i];
        }
        for(int i=Q;i>=1;i--){
            if(q[i][0]=='u') solve(X[i-1],Y[i-1],X[i],Y[i],1,0,times[i-1]);
            else if(q[i][0]=='d') solve(X[i-1],Y[i-1],X[i],Y[i],-1,0,times[i-1]);
            else if(q[i][0]=='l') solve(X[i-1],Y[i-1],X[i],Y[i],0,-1,times[i-1]);
            else solve(X[i-1],Y[i-1],X[i],Y[i],0,1,times[i-1]);
        }
        ll L=0,R=times[Q];
        rep(i,1,N*M){
            if(!Mx[i]&&s[i]=='#') R=-1;
            if(!Mx[i]) continue;
            if(s[i]=='#') L=max(L,Mx[i]);
            else R=min(R,Mx[i]-1);
        }
        if(L<=R) printf("%lld %lld
    ",L,R);
        else puts("-1 -1");
        return 0;
    }
    View Code

    K .YATP

    题意:给定带点权边权的树,定义路径的花费=路径边权和e+起点点权w[s]*终点点权w[t]。N<2e5,e,w<1e6;

    思路:首先,需要树分治。 然后得到方程dp[i]=min{ dis[i]+dis[j]+w[i]*w[j] },很显然需要斜率优化。 

          注意维护凸包的时候是需要保证w[j]是单调的,这样才能用不等式维护队尾。  由于w[i]不是对应的队尾,所以我们还要二分凸包。

         还有个问题,怎么确定我们得到的i和j不是在同一个子树呢? 因为如果在一颗子树的时候dp[i]=dis[i]+dis[j]+w[i]*w[j]-2*dis[LCA]。 其实没必要考虑这个问题,因为当LCA为根的时候会更新答案。(这一点想不到估计要很难去维护了)

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=400010;
    int Laxt[maxn],Next[maxn],To[maxn],Len[maxn],cnt;
    int sz[maxn],son[maxn],rt,all,vis[maxn],S[maxn],tot;
    ll ans[maxn],a[maxn],dis[maxn],sum; int q[maxn],top;
    bool cmp(int x,int y)
    {
        int xx=x,yy=y;
        if(a[xx]==a[yy]) return dis[xx]<dis[yy];
        return a[xx]<a[yy];
    }
    ll getans(int p,int k)
    {
        return dis[k]+dis[p]+a[p]*a[k];
    }
    void add(int u,int v,int w)
    {
        Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; Len[cnt]=w;
    }
    void dfs1(int u,int f)
    {
        sz[u]=1; son[u]=0;
        for(int i=Laxt[u];i;i=Next[i]){
             if(To[i]!=f&&!vis[To[i]]) {
                dfs1(To[i],u);
                sz[u]+=sz[To[i]];
                son[u]=max(son[u],sz[To[i]]);
             }
        }
        son[u]=max(son[u],all-son[u]);
        if(son[u]<son[rt]) rt=u;
    }
    void cal(int p)
    {
        if(top==0) return ;int L=1,R=top-1,Mid;
        ans[p]=min(ans[p],getans(p,q[top]));
        while(L<=R){
            Mid=(L+R)>>1;
            ll tmp1=getans(p,q[Mid]),tmp2=getans(p,q[Mid+1]);
            if(tmp1<tmp2) R=Mid-1,ans[p]=min(ans[p],tmp1);
            else L=Mid+1,ans[p]=min(ans[p],tmp2);
        }
    }
    void get(int u,int f)
    {
        cal(u);
        for(int i=Laxt[u];i;i=Next[i])
         if(To[i]!=f&&!vis[To[i]]) get(To[i],u);
    }
    bool check(int p){
        return (dis[p]-dis[q[top]])*(a[p]-a[q[top-1]])<=
         (dis[p]-dis[q[top-1]])*(a[p]-a[q[top]]);
    }
    void ADD(int p)
    {
        if(top&&a[p]==a[q[top]]&&dis[p]<dis[q[top]]) top--;
        while(top>1&&check(p)) top--;
        q[++top]=p;
    }
    void get(int u,int f,ll D)
    {
        dis[u]=D; sz[u]=1; S[++tot]=u;
        for(int i=Laxt[u];i;i=Next[i])
          if(To[i]!=f&&!vis[To[i]]){
             get(To[i],u,D+Len[i]);
             sz[u]+=sz[To[i]];
          }
    }
    void solve(int u,int f)
    {
        vis[u]=1; dis[u]=0;
        top=0; tot=0; S[++tot]=u;
        for(int i=Laxt[u];i;i=Next[i]){
            if(!vis[To[i]]&&To[i]!=f)
               get(To[i],u,Len[i]);
        }
        sort(S+1,S+tot+1,cmp);
        rep(i,1,tot) ADD(S[i]);
        rep(i,1,tot) cal(S[i]);
        for(int i=Laxt[u];i;i=Next[i]){
            int v=To[i];
            if(!vis[v]&&v!=f) {
                all=sz[v]; rt=0;
                dfs1(v,0); solve(rt,0);
            }
        }
    }
    int main()
    {
        int N,u,v,w;
        scanf("%d",&N); son[0]=N+1;
        rep(i,1,N) scanf("%lld",&a[i]);
        rep(i,1,N) ans[i]=a[i]*a[i];
        rep(i,1,N-1){
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w); add(v,u,w);
        }
        all=N; rt=0;
        dfs1(1,0); solve(rt,0);
        rep(i,1,N) sum+=ans[i];
        printf("%lld
    ",sum);
        return 0;
    }
    View Code
  • 相关阅读:
    C# 创建Excel并写入内容
    c#中使用excel
    C#中EXCEL表格的内容进度条实现
    WinForm c#操作Excel
    如何使用 Visual C# .NET 处理 Excel 事件
    C#与Excel的交互示例
    c#操作excel方式三:使用Microsoft.Office.Interop.Excel.dll读取Excel文件
    C#在excel中添加超链接
    ASP.NET学习笔记(3)
    ASP.NET学习笔记(4)
  • 原文地址:https://www.cnblogs.com/hua-dong/p/10536652.html
Copyright © 2020-2023  润新知