2017ICPC乌鲁木齐网络赛 全题解

来源:互联网 发布:阿里云中央仓库 编辑:程序博客网 时间:2024/04/30 02:46

A. Banana

直接暴力。

#include <bits/stdc++.h>using namespace std;typedef pair<int,int> PII;set<PII> ss;vector<int> le[55],ri[55];int main(){    int T,n,m,x,y;    scanf("%d",&T);    while (T--) {        scanf("%d%d",&n,&m);        for (int i=1;i<=50;i++) {            le[i].clear();            ri[i].clear();        }        for (int i=1;i<=n;i++) {            scanf("%d%d",&x,&y);            le[y].push_back(x);        }        for (int i=1;i<=m;i++) {            scanf("%d%d",&x,&y);            ri[x].push_back(y);        }        ss.clear();        for (int i=1;i<=50;i++) {            for (int x:le[i]) {                for (int y:ri[i]) {                    ss.insert(PII(x,y));                }            }        }        for (auto &p:ss) {            printf("%d %d\n",p.first,p.second);        }        puts("");    }    return 0;}

B. Out-out-control cars

可以看作相对运动,然后三条射线跟三条线段判相交。
要注意的是大三角形的射线不碰到小三角形也可以yes,这个我们可以通过两个都判一遍来解决。
还有一种情况(数据里好像没有)是两者相对静止并且一个套一个,按照题意也是yes。

#include <bits/stdc++.h>using namespace std;const double eps=1e-8;struct Point {    double x,y;    Point() {}    Point(double a,double b):x(a),y(b) {}    Point operator +(const Point &R) {        return Point(x+R.x,y+R.y);    }    Point operator -(const Point &R) {        return Point(x-R.x,y-R.y);    }    Point operator *(const double &R) {        return Point(x*R,y*R);    }    double operator ^(const Point &R) {        return x*R.y-y*R.x;    }    void print() {        printf("%.2f %.2f\n",x,y);    }} t[2][4];struct Line {    Point s,e;    Line() {}    Line(Point a,Point b):s(a),e(b) {}};int sgn(double x){    if(fabs(x) < eps)return 0;    if(x < 0)return -1;    else return 1;}int inner(int p,int q){    int res=0;    double area=fabs((t[p][1]-t[p][0])^(t[p][2]-t[p][0]));    for (int j=0;j<3;j++) {        double arx=fabs((t[p][1]-t[q][j])^(t[p][2]-t[q][j]));        arx+=fabs((t[p][0]-t[q][j])^(t[p][2]-t[q][j]));        arx+=fabs((t[p][0]-t[q][j])^(t[p][1]-t[q][j]));        if (sgn(arx-area)==0) ++res;    }    return res;}inline bool inter(Line l1,Line l2){    return     max(l1.s.x,l1.e.x) >= min(l2.s.x,l2.e.x) &&     max(l2.s.x,l2.e.x) >= min(l1.s.x,l1.e.x) &&     max(l1.s.y,l1.e.y) >= min(l2.s.y,l2.e.y) &&     max(l2.s.y,l2.e.y) >= min(l1.s.y,l1.e.y) &&     sgn((l2.s-l1.e)^(l1.s-l1.e))*sgn((l2.e-l1.e)^(l1.s-l1.e)) <= 0 &&     sgn((l1.s-l2.e)^(l2.s-l2.e))*sgn((l1.e-l2.e)^(l2.s-l2.e)) <= 0;}bool line_cross(){    for (int i=0;i<3;i++) {        for (int j=0;j<3;j++) {            if (inter({t[0][i],t[0][(i+1)%3]},{t[1][j],t[1][(j+1)%3]})) return true;        }    }    return false;}bool static_tri_cross(){    if (line_cross()) return true;    return inner(0,1)||inner(1,0);}bool dynamic_tri_cross(int p,int q){    Point dlt(t[q][3]-t[p][3]);    for (int i=0;i<3;i++) {        for (int j=0;j<3;j++) {            Point P0(t[p][(i+1)%3]-t[p][i]);            double rat=(P0^(t[q][j]-t[p][i]))/(dlt^P0);            double mxx=max(t[p][(i+1)%3].x,t[p][i].x);            double mnx=min(t[p][(i+1)%3].x,t[p][i].x);            double mxy=max(t[p][(i+1)%3].y,t[p][i].y);            double mny=min(t[p][(i+1)%3].y,t[p][i].y);            if (rat>=0&&mnx<=t[q][j].x+rat*dlt.x&&t[q][j].x+rat*dlt.x<=mxx                &&mny<=t[q][j].y+rat*dlt.y&&t[q][j].y+rat*dlt.y<=mxy) return true;        }    }    return false;}int main(){    int T,cas=0;    scanf("%d",&T);    while (T--) {        for (int i=0;i<2;i++) {            for (int j=0;j<4;j++) {                scanf("%lf%lf",&t[i][j].x,&t[i][j].y);            }        }        printf("Case #%d: ",++cas);        if (sgn(t[1][3].x-t[0][3].x)==0&&sgn(t[1][3].y-t[0][3].y)==0) {            puts(static_tri_cross()?"YES":"NO");        } else {            puts(dynamic_tri_cross(0,1)||dynamic_tri_cross(1,0)?"YES":"NO");        }    }    return 0;}

C. Coconut

直接模拟。

#include <bits/stdc++.h>using namespace std;int c[1005], d[1005];int main(){    int T;    scanf("%d",&T);    while(T--) {        int n, b;        scanf("%d %d", &n, &b);        for(int i = 0; i < n; ++i) {            scanf("%d", c + i);        }        for(int i = 1; i < n; ++i) {            scanf("%d", d + i);        }        int cap = c[0];        int f = 1;        for(int i = 1; i < n; ++i) {            cap -= b * d[i];            if(cap < 0) {                f = 0;                break;            }            cap += c[i];        }        if(f) puts("Yes");        else puts("No");    }    return 0;}

D. Hack Portals

poj原题。没做过,所以比赛的时候也没做
按照坐标排序后,有一个贪心的结论:i到j这一段区间中,最优情况下最后一个做的不是i就是j。
考虑区间dp,我们用dp[i][j][0]表示i到j这一段最后做i的最小花费,dp[i][j][1]表示i到j这一段最后做j的最小花费。
转移还是挺容易的,转移了以后还要再考虑一下开放的时间。

#include <bits/stdc++.h>using namespace std;const int N=1010,INF=1<<29;int dp[N][N][2];struct Classroom {    int x,t;    bool operator <(const Classroom &R) const {        return x<R.x;    }} c[N];inline void updm(int &x,int y){    if (y<x) x=y;}int main(){    int T,cas=0;    scanf("%d",&T);    while (T--) {        int n,m,b;        scanf("%d%d%d",&n,&m,&b);        for (int i=1;i<=n;i++) {            scanf("%d%d",&c[i].x,&c[i].t);        }        sort(c+1,c+n+1);        dp[1][n][0]=max(c[1].x,c[1].t);        dp[1][n][1]=max(c[n].x,c[n].t);        for (int l=n-1;l>=1;l--) {            for (int i=1;i<=n;i++) {                int j=i+l-1;                if (j>n) break;                dp[i][j][0]=dp[i][j][1]=INF;                if (i>1) {                    updm(dp[i][j][0],dp[i-1][j][0]+c[i].x-c[i-1].x);                    updm(dp[i][j][1],dp[i-1][j][0]+c[j].x-c[i-1].x);                }                if (j<n) {                    updm(dp[i][j][0],dp[i][j+1][1]+c[j+1].x-c[i].x);                    updm(dp[i][j][1],dp[i][j+1][1]+c[j+1].x-c[j].x);                }                if (dp[i][j][0]<c[i].t) dp[i][j][0]=c[i].t;                if (dp[i][j][1]<c[j].t) dp[i][j][1]=c[j].t;            }        }        int ans=INF;        for (int i=1;i<=n;i++) {            updm(ans,min(dp[i][i][0],dp[i][i][1])+abs(b-c[i].x));        }        printf("Case #%d: %d\n",++cas,ans);    }    return 0;}

E. Half-consecutive Numbers

打表。反正我们队只会打表了
q巨题解

#include <bits/stdc++.h>using namespace std;using ll = long long;ll li[] = {0, 1, 8, 49, 288, 1681, 9800, 57121, 332928, 1940449, 11309768, 65918161, 384199200, 2239277041L, 13051463048L, 76069501249L, 443365544448L, 2584123765441L, 15061377048200L, 87784138523761L, 511643454094368L, 2982076586042449L, 17380816062160328L};int main() {    int T;    scanf("%d", &T);    for(int i = 0; i != T; ++i) {        ll n;        scanf("%lld", &n);        int idx = 0;        while(li[idx] < n) ++idx;        printf("Case #%d: %lld\n", i + 1, li[idx]);    }    return 0;}

F. Islands

hdu原题。
缩点以后看出度为0的点数和入度为0的点数,答案为较大值。注意特判只有1个scc。

#include <bits/stdc++.h>using namespace std;const int MAXN = 10010;//点数const int MAXM = 100010;//边数struct Edge {    int fr,to,next;} eg[MAXM];int head[MAXN],tot;int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];//Belong数组的值是1~sccint Index,top;int scc;//强连通分量的个数bool Instack[MAXN];int num[MAXN];//各个强连通分量包含点的个数,数组编号1~scc//num数组不一定需要,结合实际情况void addedge(int u,int v){    eg[tot].fr = u;    eg[tot].to = v;    eg[tot].next = head[u];    head[u] = tot++;}void Tarjan(int u){    int v;    Low[u] = DFN[u] = ++Index;    Stack[top++] = u;    Instack[u] = true;    for(int i = head[u]; i != -1; i = eg[i].next) {        v = eg[i].to;        if( !DFN[v] ) {            Tarjan(v);            if( Low[u] > Low[v] )Low[u] = Low[v];        } else if(Instack[v] && Low[u] > DFN[v])            Low[u] = DFN[v];    }    if(Low[u] == DFN[u]) {        scc++;        do {            v = Stack[--top];            Instack[v] = false;            Belong[v] = scc;            num[scc]++;        } while( v != u);    }}void solve(int N){    memset(DFN,0,sizeof(DFN));    memset(Instack,false,sizeof(Instack));    memset(num,0,sizeof(num));    Index = scc = top = 0;    for(int i = 1; i <= N; i++)        if(!DFN[i])            Tarjan(i);}void init(){    tot = 0;    memset(head,-1,sizeof(head));}int in[MAXN],out[MAXN];int main(){    int T,n,m,x,y;    scanf("%d",&T);    while (T--) {        init();        scanf("%d%d",&n,&m);        for (int i=1;i<=m;i++) {            scanf("%d%d",&x,&y);            addedge(x,y);        }        solve(n);        if (scc==1) {            puts("0");        } else {            for (int i=1;i<=scc;i++) {                in[i]=out[i]=0;            }            for (int i=0;i<tot;i++) {                int u=Belong[eg[i].fr];                int v=Belong[eg[i].to];                if (u!=v) {                    out[u]++;                    in[v]++;                }            }            int t1=0,t2=0;            for (int i=1;i<=scc;i++) {                if (in[i]==0) t1++;                if (out[i]==0) t2++;            }            printf("%d\n",max(t1,t2));        }    }    return 0;}

G. Query on a string

因为模式串的长度很短,小于等于10,所以修改一个字符最多只会影响主串中10个位置的匹配情况。那么操作可以转化为10次单点修改和区间求和,用一个树状数组维护就可以了。有影响的那个部分可以用kmp匹配一下。

#include <bits/stdc++.h>using namespace std;const int N=100010;int nxt[N],c[N];bool b[N];char s[N],t[N];int T,n,ls,lt;void ins(int x,int d){    for (int i=x+1;i<N;i+=i&(-i)) {        c[i]+=d;    }}int get(int x){    int res=0;    for (int i=x+1;i;i-=i&(-i)) {        res+=c[i];    }    return res;}void kmp_pre(char x[],int m,int nxt[]){    int i,j;    j=nxt[0]=-1;    i=0;    while(i<m) {        while(-1!=j && x[i]!=x[j])j=nxt[j];        nxt[++i]=++j;    }}void KMP_Count(char x[],int m,char y[],int from,int to){    for (int i=from;i<=to;i++) {        if (b[i]) {            b[i]=false;            ins(i,-1);        }    }    int i=from,j=0;    while(i<to+lt) {        while(-1!=j && y[i]!=x[j]) j=nxt[j];        i++;        j++;        if(j>=m) {            b[i-m]=true;            ins(i-m,1);            j=nxt[j];        }    }}int main(){    scanf("%d",&T);    while (T--) {        scanf("%d",&n);        scanf("%s",s);        scanf("%s",t);        memset(b,0,sizeof(b));        memset(c,0,sizeof(c));        ls=strlen(s);        lt=strlen(t);        kmp_pre(t,lt,nxt);        KMP_Count(t,lt,s,0,ls-lt);        while (n--) {            char op[2];            scanf("%s",op);            if (op[0]=='Q') {                int l,r;                scanf("%d %d",&l,&r);                l--;r--;                if (r-l+1<lt) {                    puts("0");                } else {                    printf("%d\n",get(r-lt+1)-get(l-1));                }            } else {                int x;char ch;                scanf("%d %c",&x,&ch);                if (s[--x]!=ch) {                    s[x]=ch;                    KMP_Count(t,lt,s,max(0,x-lt+1),min(ls-lt,x));                }            }        }        puts("");    }    return 0;}

H. Skiing

队友一下子就读懂了,加个源跑最长路就行。

#include <bits/stdc++.h>using namespace std;const int N=10010;const int INF=1<<29;struct Edge {    int go,next,val;} eg[120000];int last[N],tot;int dp[N];bool mark[N];int C,n,m;void addedge(int x,int y,int z){    eg[tot]={y,last[x],z};    last[x]=tot++;}bool spfa(int S){    for (int i = 1; i <= n; i++) dp[i] = -INF;    memset(mark,0,sizeof(mark));    queue<int> que;    que.push(S);    dp[S] = 0;    while (!que.empty()) {        int u = que.front();        que.pop();        mark[u] = false;        for (int i=last[u];i!=-1;i=eg[i].next) {            int v=eg[i].go;            if (dp[u]+eg[i].val>dp[v]) {                dp[v]=dp[u]+eg[i].val;                if (!mark[v]) {                    mark[v]=true;                    que.push(v);                }            }        }    }    return false;}int main(){    scanf("%d",&C);    while (C--) {        int x,y,z;        scanf("%d%d",&n,&m);        tot=0;        memset(last,-1,sizeof(last));        for (int i=1;i<=m;i++) {            scanf("%d%d%d",&x,&y,&z);            addedge(x,y,z);        }        for (int i=1;i<=n;i++) {            addedge(0,i,0);        }        spfa(0);        int ans=-1;        for (int i=1;i<=n;i++) {            ans=max(ans,dp[i]);        }        printf("%d\n",ans);    }    return 0;}

I. Colored Graph

比赛的时候直接抄的论文。还抄错了好多次
回忆同色三角形的计数过程,其实是算了异色三角形的个数SS=12Vi=1ai(V1ai)
现要使得同色三角形个数尽量小,那么就要使每个点的ai(V1ai)尽量大。
由于和一定,两项自然是越接近结果越大。考虑把点集分为两个,每个集合内部的边都是白色,两个集合之间的都是黑色。
V是偶数,那么每个点的值都是一模一样的,不用调整。
下面重点讨论V是奇数的情况,记n=V2,则两个点集V1V2的大小分别为nn+1
V1中,每个点有n+1条黑边和n1条白边,这一定是可以更优的。
首先把黑边分散地改成白边,即把在in+i之间的黑边改成白边,此时V1的点会很平衡,不过V2中有n个点变成了n1条黑边和n+1条白边。
所以还能更优,当然这时不能再改n条边了,要不然就会没完了。将V2修改过的n个点分成n2组点对,各点对中的白边改成黑边,那么这n个点有n条黑边和n条白边,非常恰好。如果n也是奇数,那么就不能这么恰好了,但是大体是一样的,见代码。

#include <bits/stdc++.h>using namespace std;int g[666][666];int main(){    int T;    scanf("%d",&T);    while (T--) {        int V;        scanf("%d",&V);        int n=V/2,ans=0;        for (int i=1;i<=n;i++) {            for (int j=i+1;j<=n;j++) {                g[i][j]=g[j][i]=1;            }        }        for (int i=n+1;i<=V;i++) {            for (int j=i+1;j<=V;j++) {                g[i][j]=g[j][i]=1;            }        }        for (int i=1;i<=n;i++) {            for (int j=n+1;j<=V;j++) {                g[i][j]=g[j][i]=2;            }        }        if (V&1) {            ans=n*(n-1)*(n-2)/6+(n+1)*n*(n-1)/6-n/2;            if (n&1) {                for (int i=1;i<=n-1;i++) {                    g[i][i+n]=g[i+n][i]=1;                }                for (int i=n+1;i<=n+n-2;i+=2) {                    g[i][i+1]=g[i+1][i]=2;                }            } else {                for (int i=1;i<=n;i++) {                    g[i][i+n]=g[i+n][i]=1;                }                for (int i=n+1;i<=n+n-1;i+=2) {                    g[i][i+1]=g[i+1][i]=2;                }            }        } else {            ans=n*(n-1)*(n-2)/3;        }        printf("%d\n",ans);        for (int i=1;i<=V;i++) {            for (int j=1;j<=V;j++) {                printf("%d%c",g[i][j]," \n"[j==V]);            }        }    }    return 0;}

J. Our Journey of Dalian Ends

hdu原题。
中转站为源做一遍最小费用流。

#include <bits/stdc++.h>using namespace std;const int MAXN = 20010;const int MAXM = 100000;const int INF = 0x3f3f3f3f;struct Edge {    int to,next,cap,flow,cost;} eg[MAXM];int head[MAXN],tol;int pre[MAXN],dis[MAXN];bool vis[MAXN];int N;//节点总个数,节点编号从0~N-1void addedge(int u,int v,int cap,int cost){    eg[tol].to = v;    eg[tol].cap = cap;    eg[tol].cost = cost;    eg[tol].flow = 0;    eg[tol].next = head[u];    head[u] = tol++;    eg[tol].to = u;    eg[tol].cap = 0;    eg[tol].cost = -cost;    eg[tol].flow = 0;    eg[tol].next = head[v];    head[v] = tol++;}bool spfa(int s,int t){    queue<int>q;    for(int i = 0; i < N; i++) {        dis[i] = INF;        vis[i] = false;        pre[i] = -1;    }    dis[s] = 0;    vis[s] = true;    q.push(s);    while(!q.empty()) {        int u = q.front();        q.pop();        vis[u] = false;        for(int i = head[u]; i != -1; i = eg[i].next) {            int v = eg[i].to;            if(eg[i].cap > eg[i].flow &&                    dis[v] > dis[u] + eg[i].cost ) {                dis[v] = dis[u] + eg[i].cost;                pre[v] = i;                if(!vis[v]) {                    vis[v] = true;                    q.push(v);                }            }        }    }    if(pre[t] == -1)return false;    else return true;}//返回的是最大流,cost存的是最小费用int minCostMaxflow(int s,int t,int &cost){    int flow = 0;    cost = 0;    while(spfa(s,t)) {        int Min = INF;        for(int i = pre[t]; i != -1; i = pre[eg[i^1].to]) {            if(Min > eg[i].cap - eg[i].flow)                Min = eg[i].cap - eg[i].flow;        }        for(int i = pre[t]; i != -1; i = pre[eg[i^1].to]) {            eg[i].flow += Min;            eg[i^1].flow -= Min;            cost += eg[i].cost * Min;        }        flow += Min;    }    return flow;}char s[10010][1000],t[10010][1000];int c[10010];map<string,int> mp;int main(){    int C;    scanf("%d",&C);    while (C--) {        int m,n=0;        scanf("%d",&m);        mp.clear();        tol = 0;        memset(head,-1,sizeof(head));        for (int i=0;i<m;i++) {            scanf("%s%s%d",s[i],t[i],&c[i]);            if (mp.find(s[i])==mp.end()) {                mp[s[i]]=n++;            }            if (mp.find(t[i])==mp.end()) {                mp[t[i]]=n++;            }        }        int S=mp["Shanghai"];        for (int i=0;i<m;i++) {            int u=mp[s[i]],v=mp[t[i]];            addedge(u,n+v,1,c[i]);            addedge(v,n+u,1,c[i]);        }        for (int i=0;i<n;i++) {            addedge(n+i,i,1,0);        }        int T=n+n;N=T+1;        addedge(n+mp["Dalian"],T,1,0);        addedge(n+mp["Xian"],T,1,0);        int ans=0;        if (minCostMaxflow(S,T,ans)<2) {            puts("-1");            continue;        }        printf("%d\n",ans);    }    return 0;}
阅读全文
0 0
原创粉丝点击