南阳CCPC补题:UESTC 1225(dp……另附对拍器)+UESTC 1220(最短路,费用流)+UESTC 1219(高斯消元)

1225

题目:有一栋楼,每层楼上有t个人打网球,有p个人游泳,但是每层只能建一种健身设施。每个人的花费是他到最近的对应设施的楼层距离。问最小总花费。

思路:dp[i][j][k]表示做到第i层的时候选j,和j不同的设施最近在第k层的最小花费,那么如果该层选得和上面一样那么k不变,这个好转移。如果和上一层不一样,那么k层到当前层i的玩家会有一部分重新分布(他们的选择显然以中点为准),这个需要预处理一下(我写得很恶心。。。)。然后wa了整整一天,最后找了个代码对拍了一下才过(基本上完全是对的,,有个初始化没做。。。真是哔了狗。。)。这个对拍器真是好使啊。。。。

丑陋的代码。。

/*
* @author:  Cwind
*/
#include <bits/stdc++.h>

using namespace std;
#define IOS std::ios::sync_with_stdio (false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps (1e-10)
#define IINF (1<<29)
#define LINF (1ll<<49)
#define INF (1000000300)
#define FINF (1e9)
#define clr(x) memset((x),0,sizeof (x))
#define cp(a,b) memcpy((a),(b),sizeof (b))
#define mset(x,v) memset((x),(v),sizeof (x))

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;

const int maxn=5000;
int T;
int n;
ll t[maxn],p[maxn];
ll ts[2][maxn],ps[2][maxn];
ll tmm[2][maxn],pm[2][maxn];
ll dp[2][2][maxn];
void cal(){
    for(int i=1;i<=n;i++){
        ts[0][i]=ts[0][i-1]+t[i];
        ps[0][i]=ps[0][i-1]+p[i];
        tmm[0][i]=tmm[0][i-1]+t[i]*i;
        pm[0][i]=pm[0][i-1]+p[i]*i;
    }
    ts[1][n+1]=ps[1][n+1]=0;
    tmm[1][n+1]=pm[1][n+1]=0;
    for(int i=n;i>=1;i--){
        ts[1][i]=ts[1][i+1]+t[i];
        ps[1][i]=ps[1][i+1]+p[i];
        tmm[1][i]=tmm[1][i+1]+t[i]*(n-i+1);
        pm[1][i]=pm[1][i+1]+p[i]*(n-i+1);
    }
}
ll get(bool o,int l,int r){
    int m=(r+l)/2;
    if(o){
        if(l==0) return tmm[1][1]-tmm[1][r+1]-(ts[1][1]-ts[1][r+1])*(n-r+1);
        ll oc=tmm[0][r]-tmm[0][l-1]-(ts[0][r]-ts[0][l-1])*l;
        ll nc=tmm[0][m]-tmm[0][l-1]-(ts[0][m]-ts[0][l-1])*l+tmm[1][m+1]-tmm[1][r+1]
                -(ts[1][m+1]-ts[1][r+1])*(n-r+1)+t[r]*(r-l);
        return nc-oc;
    }else{
        if(l==0) return pm[1][1]-pm[1][r+1]-(ps[1][1]-ps[1][r+1])*(n-r+1);
        ll oc=pm[0][r]-pm[0][l-1]-(ps[0][r]-ps[0][l-1])*l;
        ll nc=pm[0][m]-pm[0][l-1]-(ps[0][m]-ps[0][l-1])*l+pm[1][m+1]-pm[1][r+1]
                -(ps[1][m+1]-ps[1][r+1])*(n-r+1)+p[r]*(r-l);
        return nc-oc;
    }
}
int cas;
int main(){
    //freopen("/home/slyfc/CppFiles/in","r",stdin);
    //freopen("/home/slyfc/CppFiles/out","w",stdout);
    cin>>T;
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)      
            scanf("%lld%lld",&t[i],&p[i]);
        cal();
        for(int i=0;i<2;i++)
            for(int k=0;k<2;k++)
                for(int j=0;j<maxn;j++)
                    dp[i][k][j]=LINF;
        dp[0][0][0]=dp[0][1][0]=0;
        dp[1][0][0]=dp[1][1][0]=0;
        bool f=0;
        for(int i=2;i<=n;i++){
            f^=1;
            for(int j=1;j<i-1;j++){
                dp[f][0][j]=dp[f^1][0][j]+p[i]*(i-j);
                dp[f][1][j]=dp[f^1][1][j]+t[i]*(i-j);
            }
            for(int j=0;j<i-1;j++){
                dp[f][0][i-1]=min(dp[f][0][i-1],dp[f^1][1][j]+p[i]+get(1,j,i));
                dp[f][1][i-1]=min(dp[f][1][i-1],dp[f^1][0][j]+t[i]+get(0,j,i));
            }
        }
        ll ans=LINF;
        for(int i=1;i<n;i++){
            ans=min(ans,dp[f][1][i]);
            ans=min(ans,dp[f][0][i]);
        }
        printf("Case #%d: %lld\n",++cas,ans);
    }
    return 0;    
}
View Code

对拍器

while true; do
./make>inData #出数据
./myCpp<inData>myOut #被测程序
./stdCpp<inData>stdOut #正确(暴力)程序
if diff myOut stdOut; then #比较两个输出文件
printf AC #结果相同显示AC
else
echo WA #结果不同显示WA,并退出
#cat tmp.out tmp2.out
exit 0
fi #if的结束标志,与C语言相反,0为真
done # while的结束标志

#BY NICK WONG 2014-08-29
#在终端下,进入当前目录,输入"sh ./nick.sh",(其中nick.sh为当前shell脚本名) '#'表示单行注释
#diff在两文件相同时返回空串

对拍器转自:http://blog.csdn.net/nickwong_/article/details/38931579

1220

题目:曹操和袁绍决战,曹操当然想赢啦。但是袁绍势力太大,曹操征兵也会有同样多的人跑去袁绍那边= =。有M个战场,N个村庄。曹操在村庄i征一个兵花费w,可以送到x战场去,同时有同样数量的兵会跑去y战场的袁绍阵营。每个战场有个重要度,重要度为0表示无所谓输赢,为1表示不能输(己方人数不少于敌方),为2表示己方人数必须大于敌方。问能否赢和最小花费。

思路:考虑每个战场己方的净人数,那么实际上征兵对应于x战场增加1,y战场减少1,最后重要战场的净人数大于1(实际上肯定是正好为1)。那么可以建图跑费用流,每个y向x连边,s向重要度0的连边,重要度为2的向t连边。这样每个重要度1的点由于不会与s和t连边,净流为0(人数为0)。重要度为2的点满流(流量1)可以胜利。对应的由于连出s的边净流量不为0,正好对应着重要度为0的点(流量任意)。然后这样会t。实际上费用流是每次沿最短路增广的做法,这个题实际上也是最短路。因为实际上本题不会出现已经分配的流量再退回去的情况。对于任何一条从s到重要度2的点的最短路,必然从重要度为0的点出发,途径若干重要度1的点最后到达重要读2的点。这个过程中总流量没有限制,增加的费用其实是最短路的长度。对应从起点转移1个人到终点,中间点的人数并没有变化(净人数的想法很关键!!)

/*
* @author:  Cwind
*/
#include <bits/stdc++.h>

using namespace std;
#define IOS std::ios::sync_with_stdio (false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps (1e-10)
#define IINF (1<<29)
#define LINF (1ll<<49)
#define INF (1000000300)
#define FINF (1e9)
#define clr(x) memset((x),0,sizeof (x))
#define cp(a,b) memcpy((a),(b),sizeof (b))
#define mset(x,v) memset((x),(v),sizeof (x))

typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,int> P;
typedef pair<int,int> pp;

const int maxn=1e5+300;
const ll inf=1e18;
int T;
int N,M;
int x[maxn],y[maxn],c[maxn],w[maxn];
int cas;
const int s=maxn-1;
ll dis[maxn];
vector<pp> G[maxn];
void dij(){
    fill(dis,dis+maxn,inf);
    priority_queue<P,vector<P>,greater<P> > Q;
    dis[s]=0;
    Q.push(P(0,s));
    while(!Q.empty()){
        int u=Q.top().se;
        ll d=Q.top().fs;Q.pop();
        if(d>dis[u]) continue;
        for(int i=0;i<G[u].size();i++){
            int v=G[u][i].fs,d=G[u][i].se;
            if(dis[v]>dis[u]+d){
                dis[v]=dis[u]+d;
                Q.push(P(dis[v],v));
            }
        }
    }
}
void init(){
    for(int i=0;i<=M;i++){
        G[i].clear();
    }
    G[s].clear();
}
void addedge(int from,int to,int d){G[from].pb(pp(to,d));}
int main(){
    freopen("/home/slyfc/CppFiles/in","r",stdin);
    //freopen("/home/slyfc/CppFiles/out","w",stdout);
    cin>>T;
    while(T--){
        scanf("%d%d",&N,&M);
        init();
        int f=0;
        for(int i=1;i<=N;i++)
            scanf("%d",&x[i]);
        for(int i=1;i<=N;i++)
            scanf("%d",&y[i]);
        for(int i=1;i<=N;i++)
            scanf("%d",&c[i]);
        for(int i=1;i<=M;i++)
            scanf("%d",&w[i]);
        for(int i=1;i<=M;i++)
            if(w[i]==0){
                addedge(s,i,0);
            }
        for(int i=1;i<=N;i++){
            addedge(y[i],x[i],c[i]);
        }
        dij();
        ll cost=0;
        for(int i=1;i<=M;i++){
            if(w[i]<2) continue;
            if(dis[i]==inf){
                cost=-1;
                break;
            }
            cost+=dis[i];
        }
        printf("Case #%d: %lld\n",++cas,cost);
    }
    return 0;    
}
View Code

 1219

题目:求一个无向图中的一个环路使得路径上的权值异或和最大。

思路:“任意一个环路都是由某些环路线性组合而成",那么我们从任意点出发求出部分环路的异或和,然后高斯消元,最后再求最大值即可。

/*
* @author:  Cwind
*/
#include <bits/stdc++.h>

using namespace std;
#define IOS std::ios::sync_with_stdio (false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps (1e-10)
#define IINF (1<<29)
#define LINF (1ll<<49)
#define INF (1000000300)
#define FINF (1e9)
#define clr(x) memset((x),0,sizeof (x))
#define cp(a,b) memcpy((a),(b),sizeof (b))
#define mset(x,v) memset((x),(v),sizeof (x))

typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,int> P;
typedef pair<int,int> pp;

const int maxn=5e4+300;
int T;
struct EDGE{
    int to;
    ll w;
    EDGE(int to,ull w):to(to),w(w){}
};
vector<EDGE> G[maxn];
int n,m;
bool vis[maxn];
ll val[600];
ll up[500];
ll xx[maxn];
void init(){
    for(int i=0;i<=n;i++){
        G[i].clear();
    }
    memset(vis,0,sizeof vis);
    memset(val,0,sizeof val);
    ll x=1;
    up[0]=x;
    for(int i=1;i<63;i++){
        x<<=1;
        up[i]=x;
    }
}
void add(ll v){
    if(v==0) return;
    int bit=0;
    for(int i=0;i<63;i++){
        if(v>=up[i]) bit=i;
    }
    for(int i=bit;i>=0;i--){
        if(v==0) break;
        if(v<up[i]) continue;
        if(val[i]==0){
            val[i]=v;
            break;
        }else{
            v=val[i]^v;
        }
    }
}
void dfs(int v,ll num){
    vis[v]=1;
    xx[v]=num;
    for(int i=0;i<G[v].size();i++){
        EDGE &e=G[v][i];
        if(!vis[e.to]) dfs(e.to,num^e.w);
        else add(num^e.w^xx[e.to]);
    }
}
int cas;
int main(){
    freopen("/home/slyfc/CppFiles/in","r",stdin);
    //freopen("/home/slyfc/CppFiles/out","w",stdout);
    cin>>T;
    while(T--){
        scanf("%d%d",&n,&m);
        init();
        int s;
        for(int i=0;i<m;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            ull c;
            cin>>c;
            G[a].pb(EDGE(b,c));
            G[b].pb(EDGE(a,c));
            s=a;
        }
        dfs(s,0);
        ll ans=0;
        for(int i=63;i>=0;i--)
            ans=max(ans,ans^val[i]);
        printf("Case #%d: %lld\n",++cas,ans);
    }
    return 0;    
}
View Code

 

posted @ 2015-10-30 20:03  PlusSeven  阅读(254)  评论(0编辑  收藏  举报