ACM/ICPC乌鲁木齐2017解题报告

来源:互联网 发布:java中bigdecimal相加 编辑:程序博客网 时间:2024/04/27 22:04

两位队友的博客:

http://hzwer.com/

http://kuribohg.github.io/

BEH三题是我写的,其余题目鸣谢二位大腿。

A:Banana

开始样例错了。
代码:
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>using namespace std;int T,n,m,a[100][100],b[100][100],ans[100][100];int main(){    scanf("%d",&T);    while(T--)    {        scanf("%d%d",&n,&m);        for(int i=1;i<=50;i++)            for(int j=1;j<=50;j++)                a[i][j]=0,b[i][j]=0,ans[i][j]=0;        for(int i=1,x,y;i<=n;i++)            scanf("%d%d",&x,&y),a[x][y]=1;        for(int i=1,x,y;i<=m;i++)            scanf("%d%d",&x,&y),b[x][y]=1;        for(int i=1;i<=50;i++)            for(int j=1;j<=50;j++)                for(int k=1;k<=50;k++)                    if(a[i][k]&&b[k][j]) ans[i][j]=1;        for(int i=1;i<=50;i++)            for(int j=1;j<=50;j++)                if(ans[i][j]) printf("%d %d\n",i,j);        puts("");    }    return 0;}


B:Out-out control cars

题意:两个三角形匀速直线运动,问是否会撞上。

题解:只需单独计算每个三角形的某个顶点是否会和另一个三角形撞上,进一步地,只需要计算每个三角形的每个顶点是否与另一个三角形的每一条边撞上。方法就是计算一下相对速度(建立一个坐标系,相当于三角形静止,顶点匀速直线运动),然后问题转化为射线和线段相交,解二元一次方程组即可。

代码:

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;const double eps=1e-10;bool solve_mat(double a00,double a01,double a10,double a11,double b0,double b1,double &x0,double &x1){    double det=a00*a11-a01*a10;    if(fabs(det)<eps) return false;    x0=(+a11*b0-a01*b1)/det;    x1=(-a10*b0+a00*b1)/det;    return true;}class Point{public:    double x,y;    Point(){}    Point(double _x,double _y):x(_x),y(_y){}    Point operator + (const Point &b){return Point(x+b.x,y+b.y);}    Point operator - (const Point &b){return Point(x-b.x,y-b.y);}    Point operator * (double b){return Point(x*b,y*b);}};bool solve_prm(Point p0,Point d0,Point p1,Point d1,double &t0,double &t1){    //p0+d0*t0 = p1+d1*t1    double a00=d0.x,a01=-d1.x;    double a10=d0.y,a11=-d1.y;    double b0=p1.x-p0.x;    double b1=p1.y-p0.y;    return solve_mat(a00,a01,a10,a11,b0,b1,t0,t1);}bool hit(Point p,Point d,Point a,Point b){    //if ray p+dt hits [a,b]    double t0,t1;    if(!solve_prm(p,d,a,b-a,t0,t1)) return false;    if(t0<0) return false;    if(t1<0||t1>1) return false;    return true;}bool work(void){    Point A[3],Ad;    Point B[3],Bd;    for(int i=0;i<3;i++) scanf("%lf%lf",&A[i].x,&A[i].y);scanf("%lf%lf",&Ad.x,&Ad.y);    for(int i=0;i<3;i++) scanf("%lf%lf",&B[i].x,&B[i].y);scanf("%lf%lf",&Bd.x,&Bd.y);    for(int i=0;i<3;i++){        for(int j=0;j<3;j++){            Point p=A[i];            Point d=Ad-Bd;            Point a=B[j],b=B[(j+1)%3];            if(hit(p,d,a,b)) return true;            p=B[i];            d=Bd-Ad;            a=A[j],b=A[(j+1)%3];            if(hit(p,d,a,b)) return true;        }    }    return false;}int main(){    int T;scanf("%d",&T);    for(int tot=1;tot<=T;tot++){        printf("Case #%d: ",tot);        if(work()) printf("YES\n");        else printf("NO\n");    }    return 0;}

C:Coconut

代码:
#include<map>#include<cmath>#include<ctime>#include<queue>#include<stack>#include<cstdio>#include<vector>#include<bitset>#include<cstring>#include<iostream>#include<algorithm>#define mod 1000000007#define ll long longusing namespace std;ll read(){    ll x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}int T;int n, b;int c[1005], d[1005];int main(){    T = read();    while(T--)    {        n = read(); b = read();        for(int i = 1; i <= n; i++)            c[i] = read();        for(int i = 1; i < n; i++)            d[i] = read();        int tot = 0;        bool flag = 1;        for(int i = 1; i < n; i++)        {            tot += c[i] - d[i] * b;            if(tot < 0)            {                puts("No");                flag = 0;                break;            }        }        if(flag)puts("Yes");    }    return 0;}

D:Hack Portals

题意:数轴上有一堆点。你可以每秒走长度1,也可以静止。每个点有一个出现时间,你必须按任意顺序访问所有的点(在其出现之后),你一开始在0,最后回到坐标K,问最少需要多少秒。坐标均为整数,点数和坐标不超过1000,出现时间不超过32768.
题解:枚举一开始在0静止的时长。然后一定是:先走到没被访问的最右点,再走到没被访问的最左点……直到完成。该过程可以O(n)模拟,见代码。
代码:
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cstdlib>using namespace std;const int MAXN=1010;int T,n,L,K,gol[MAXN],x[MAXN],y[MAXN];int q[MAXN],l,r;bool used[MAXN];bool cmp(int i,int j){    return x[i]<x[j];}int main(){    scanf("%d",&T);    for(int cas=1;cas<=T;cas++)    {        printf("Case #%d: ",cas);        scanf("%d%d%d",&n,&L,&K);        for(int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);        for(int i=1;i<=n;i++) q[i]=i;        sort(q+1,q+n+1,cmp);        int ans=1000000000;        for(int t=0;t<=32768;t++)        {            l=1,r=n;            bool ok=true;            int nowx=0,nowy=t;            while(true)            {                if(nowy+x[q[r]]-nowx<y[q[r]]) {ok=false;break;}                nowy+=x[q[r]]-nowx,nowx=x[q[r]];                while(l<=r&&y[q[l]]-x[q[l]]<=nowy-nowx) l++;                if(l>r) break;                if(nowy+nowx-x[q[l]]<y[q[l]]) {ok=false;break;}                nowy+=nowx-x[q[l]],nowx=x[q[l]];                while(r>=l&&x[q[r]]+y[q[r]]<=nowx+nowy) r--;                if(l>r) break;            }            if(ok) ans=min(ans,nowy+abs(nowx-K));        }        printf("%d\n",ans);    }    return 0;}


E:Half-consecutive Numbers

题意:定义序列t[i]=i*(i+1)/2,每次给定一个n,问t序列第n项以后的第一个完全平方数是什么,n<=10^16。
题解:i和i+1/2互质,故要么i=n^2, i+1=2m^2,或者i=2m^2, i+1=n^2。枚举n,n不超过10^8,  打表。
代码:
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#include<set>using namespace std;typedef long long LL;bool test(LL x){    LL r=sqrt(x+0.5);    return r*r==x;}bool check(LL n,LL k){    LL m=n*n+k;    return test(m/2);}LL lis[100]={0LL,1LL,8LL,49LL,288LL,1681LL,9800LL,57121LL,332928LL,1940449LL,11309768LL,65918161LL,384199200LL,2239277041LL,13051463048LL,76069501249LL,443365544448LL,2584123765441LL,15061377048200LL,87784138523761LL,511643454094368LL,2982076586042449LL,17380816062160328LL,};int main(){    /*freopen("ilis.out","w",stdout);    LL mx=1e16;    cout<<check(3,-1)<<endl;    for(LL i=1;;i+=2){        if(check(i,-1)){            cout<<(i*i-1)<<"LL,";            if(i*i-1>=mx) break;        }        if(check(i,1)){            cout<<i*i<<"LL,";            if(i*i>=mx) break;        }    }*/    int T;scanf("%d",&T);    for(int tot=1;tot<=T;tot++){        LL n;scanf("%lld",&n);        int i;for(i=0;lis[i]<n;i++);        printf("Case #%d: %lld\n",tot,lis[i]);    }    return 0;}

F:Islands

题解:缩点之后,所有块的入度、出度取max。
代码:
#include<map>#include<set>#include<cmath>#include<stack>#include<deque>#include<queue>#include<cstdio>#include<vector>#include<cstring>#include<cstdlib>#include<iostream>#include<algorithm>#define eps 1e-12#define mp(a, b) make_pair(a, b)#define ll long long#define inf 9000000000000000000LL#define mod 1000000007#define pa pair<int,int>using namespace std;int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}int T, n, m;int ind, scc, top;int in[10005], out[10005];int low[10005], dfn[10005], q[10005], bl[10005];bool inq[10005];vector<int>e[10005];void tarjan(int x){low[x] = dfn[x] = ++ind;    q[++top]=x; inq[x]=1;        for(int i = 0; i < e[x].size(); i++)if(!dfn[e[x][i]]){tarjan(e[x][i]);low[x] = min(low[x], low[e[x][i]]);}else if(inq[e[x][i]])            low[x] = min(low[x], dfn[e[x][i]]);    if(low[x] == dfn[x])    {        int now = 0; scc++;        while(x != now)        {            now = q[top]; top--;            bl[now] = scc;            inq[now] = 0;        }    }}int main(){    T = read();    while(T--)    {        scc = ind = 0;        memset(dfn, 0, sizeof(dfn));        memset(low, 0, sizeof(low));        memset(out, 0, sizeof(out));        memset(in, 0, sizeof(in));        memset(bl, 0, sizeof(bl));        for(int i = 1; i <= 10000; i++)            e[i].clear();                n = read(); m = read();        for(int i = 1; i <= m; i++)        {            int u = read(), v =read();            e[u].push_back(v);        }        for(int i = 1; i <= n; i++)            if(!dfn[i])tarjan(i);        for(int i = 1; i <= n; i++)            for(int j = 0; j < e[i].size(); j++)                if(bl[i] != bl[e[i][j]])                    in[bl[e[i][j]]]++, out[bl[i]]++;        int t1 = 0, t2 = 0;        for(int i = 1; i <= scc; i++)        {            if(!in[i])t1++;            if(!out[i])t2++;        }        if(scc == 1)puts("0");        else printf("%d\n", max(t1, t2));    }    return 0;}

G:Query on String

题意:给字符串S,T,T的长度不超过10.每次询问S[l..r]中有多少个T,或者改S的一位。
题解:先预处理一下,就变成了区间和查询。改S的一位至多影响10个值,用树状数组即可。
代码:
#include<map>#include<set>#include<cmath>#include<stack>#include<deque>#include<queue>#include<cstdio>#include<vector>#include<cstring>#include<cstdlib>#include<iostream>#include<algorithm>#define eps 1e-12#define mp(a, b) make_pair(a, b)#define ll long long#define inf 9000000000000000000LL#define mod 1000000007#define pa pair<int,int>using namespace std;int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}int q, n, m, Tes;int t[100005];void add(int x, int val){    x++;    if(val == 0)return;    for(int i = x; i <= n; i += i & -i)        t[i] += val;}int query(int x){    x++;    int ans = 0;    for(int i = x; i; i -= i & -i)        ans += t[i];    return ans;}string S, T;bool check(int x){    if(x + m - 1 > n - 1)return 0;    for(int i = 0; i < m; i++)        if(S[x + i] != T[i])return 0;    return 1;}int main(){    Tes = read();    while(Tes--)    {        memset(t, 0, sizeof(t));        q = read();        cin >> S; n = S.length();        cin >> T; m = T.length();        for(int i = 0; i < n; i++)            add(i, check(i));        char ch[2];        while(q--)        {            scanf("%s", ch);            if(ch[0] == 'Q')            {                int l = read() - 1, r = read() - 1;                r = r - m + 1;                if(l > r)                {                    puts("0");                }                else                {                    int ans = query(r) - query(l - 1);                    printf("%d\n", ans);                }            }            else            {                int pos = read() - 1;                scanf("%s", ch);                for(int i = max(0, pos - m + 1); i <= pos; i++)                    add(i, -check(i));                S[pos] = ch[0];                for(int i = max(0, pos - m + 1); i <= pos; i++)                    add(i, check(i));            }        }        puts("");    }    return 0;}


H:Skiing

题意:DAG最长路。DAG是题目保证的……
题解:随便写。我抄了个SPFA的板子。
代码:
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<vector>#include<queue>using namespace std;const int INF=0x7fffffff/2;const int SIZEN=10010;int N,M;vector<pair<int,int> > c[SIZEN];void SPFA(vector<pair<int,int> > c[SIZEN],int N,int S,int f[SIZEN]){//图中顶点0~N-1,邻接表为c,以S出发,结果存于fqueue<int> Q;bool inq[SIZEN]={0};for(int i=0;i<=N+1;i++) f[i]=-INF;f[S]=0,inq[S]=true,Q.push(S);while(!Q.empty()){int x=Q.front();Q.pop();inq[x]=false;for(int i=0;i<c[x].size();i++){int u=c[x][i].first;if(f[x]+c[x][i].second>f[u]){f[u]=f[x]+c[x][i].second;if(!inq[u]){inq[u]=true;Q.push(u);}}}}}int f[SIZEN];void work(void){    SPFA(c,N,0,f);    printf("%d\n",f[N+1]);}void read(void){    scanf("%d%d",&N,&M);    int a,b,w;    for(int i=0;i<=N+1;i++) c[i].clear();    for(int i=1;i<=M;i++){        scanf("%d%d%d",&a,&b,&w);        c[a].push_back(make_pair(b,w));    }    for(int i=1;i<=N;i++){        c[0].push_back(make_pair(i,0));        c[i].push_back(make_pair(N+1,0));    }}int main(){    int T;scanf("%d",&T);    while(T--){        read();        work();    }    return 0;}

I:Colored Graph

题意:给一张N个点完全图,求把每条边黑白染色,最少生成多少个纯色三角形。

代码:

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>using namespace std;int T,n;int d[510][510],a[510];int gcd(int a,int b){    if(b==0) return a;    return gcd(b,a%b);}void print(){    for(int i=1;i<=n;i++)        for(int j=1;j<=n;j++)        {            printf("%d",d[i][j]);            if(j!=n) putchar(' ');            else printf("\n");        }}int main(){    scanf("%d",&T);    while(T--)    {        scanf("%d",&n);        if(n%2==0)        {            for(int i=1;i<=n/2;i++)                for(int j=1;j<=n/2;j++)                    if(i!=j) d[i][j]=2;            for(int i=n/2+1;i<=n;i++)                for(int j=n/2+1;j<=n;j++)                    if(i!=j) d[i][j]=2;            for(int i=1;i<=n;i++)                for(int j=1;j<=n;j++)                    if(i!=j&&d[i][j]==0) d[i][j]=1;        }        else if(n%4==1)        {            int total=0;            for(int i=1;i<n;i++)            {                int xx=gcd(i,n);                total++;                for(int st=1;st<=xx;st++)                {                    int now=st;                    for(int j=1;j<=n/xx;j++)                    {                        int p=(now+i)%n;                        if(p==0) p=n;                        d[now][p]=d[p][now]=1;                        now=p;                    }                }                if(total==(n-1)/4) break;            }            for(int i=1;i<=n;i++)                for(int j=1;j<=n;j++)                    if(i!=j&&d[i][j]==0) d[i][j]=2;        }        else        {            int m=n-2;            int total=0;            for(int i=1;i<m;i++)            {                int xx=gcd(i,m);                total++;                for(int st=1;st<=xx;st++)                {                    int now=st;                    for(int j=1;j<=m;j++)                    {                        int p=(now+i)%m;                        if(p==0) p=m;                        d[now][p]=d[p][now]=1;                        now=p;                    }                }                if(total==(m-1)/4) break;            }            for(int i=1;i<=m;i++)                for(int j=1;j<=m;j++)                    if(i!=j&&d[i][j]==0) d[i][j]=2;            for(int i=1;i<=m/2;i++) d[m+1][i]=1,d[i][m+1]=1;            for(int i=m/2+1;i<=m;i++) d[m+1][i]=2,d[i][m+1]=2;            for(int i=1;i<=m/2+1;i++) d[m+2][i]=2,d[i][m+2]=2;            for(int i=m/2+2;i<=m;i++) d[m+2][i]=1,d[i][m+2]=1;            d[m+1][m+2]=d[m+2][m+1]=1;        }        for(int i=1;i<=n;i++)            for(int j=1;j<=n;j++)                if(d[i][j]==1) a[i]++;        int ans=n*(n-1)*(n-2)/6;        int sum=0;        for(int i=1;i<=n;i++) sum+=a[i]*(n-1-a[i]);        ans-=sum/2;        printf("%d\n",ans);        print();        for(int i=1;i<=n;i++)            for(int j=1;j<=n;j++)                d[i][j]=0;        for(int i=1;i<=n;i++) a[i]=0;    }    return 0;}

J:Our Journey of Dalian Ends

题意:一张无向图,求从1走到N,必须经过K,每个节点至多经过一次的最短路。

题解:费用流。

代码:

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<string>#include<map>#define next next2using namespace std;const int INF=1000000000;int TT,m,tot;char str1[100010],str2[100010];map<string,int> M;int getid(char s[]){    string str=string(s);    if(M.find(str)==M.end())    {        M[str]=++tot;        return tot;    }    else return M[str];}struct Edge{    int x,y,w;    Edge(){}    Edge(int x,int y,int w):x(x),y(y),w(w){}}E[100010];const int MAXN=100010;const int MAXM=200010;int S,T;int head[MAXN],fr[MAXM],to[MAXM],next[MAXM],from[MAXM],cnt=1;int w[MAXM],c[MAXM],cost,flow,dis[MAXN];int q[MAXN],l,r;bool used[MAXN];inline void adde(int f,int t,int ww,int cc){    cnt++,fr[cnt]=f,to[cnt]=t,w[cnt]=ww,c[cnt]=cc,next[cnt]=head[f],head[f]=cnt;    cnt++,fr[cnt]=t,to[cnt]=f,w[cnt]=0LL,c[cnt]=-cc,next[cnt]=head[t],head[t]=cnt;}bool SPFA(){for(int i=1;i<=T;i++) dis[i]=INF,from[i]=0;dis[S]=0,used[S]=true,q[l=0]=S,r=1;while(l!=r){int x=q[l++];if(l==T) l=0;used[x]=false;for(int i=head[x];i;i=next[i])if(w[i]>0&&dis[to[i]]>dis[x]+c[i]){dis[to[i]]=dis[x]+c[i];from[to[i]]=i;if(!used[to[i]]){q[r++]=to[i];if(r==T) r=0;used[to[i]]=true;}}}return dis[T]!=INF;}void DFS(){int x=INF;for(int i=from[T];i;i=from[fr[i]])x=min(x,w[i]);flow+=x;for(int i=from[T];i;i=from[fr[i]])w[i]-=x,w[i^1]+=x,cost+=c[i]*x;}void MCMF(){while(SPFA())DFS();}int main(){    scanf("%d",&TT);    while(TT--)    {        scanf("%d",&m);        for(int i=1;i<=m;i++)        {            int www;            scanf("%s%s%d",str1,str2,&www);            int x=getid(str1),y=getid(str2);            E[i]=Edge(x,y,www);        }        for(int i=1;i<=tot;i++) adde(i,tot+i,1,0);        for(int i=1;i<=m;i++) adde(tot+E[i].x,E[i].y,INF,E[i].w),adde(tot+E[i].y,E[i].x,INF,E[i].w);        int dalian=getid("Dalian");        int xian=getid("Xian");        int shanghai=getid("Shanghai");        S=2*tot+1,T=2*tot+2;        adde(S,shanghai,2,0);        adde(shanghai,tot+shanghai,1,0);        adde(tot+dalian,T,1,0);        adde(tot+xian,T,1,0);        MCMF();        if(flow!=2) puts("-1");        else printf("%d\n",cost);        M.clear();        for(int i=1;i<=T;i++) head[i]=0;        cnt=1;        flow=0,cost=0;        tot=0;    }    return 0;}



阅读全文
0 0
原创粉丝点击