zoukankan      html  css  js  c++  java
  • Gym101485: NWERC 2015(队内第6次训练)

    A .Assigning Workstations

    题意:给定N个人的工作时间和工作时长,我们可以假设有无数台工作机器,如果一台机器超过M时间未使用就会关闭,那么我们怎么安排机器的使用,使得需要开启机器的次数最少。

    思路:贪心,维护一个时间队列q,维护一个单调队列q1; 前者表示没有使用了但还没关闭的机器队列,后者表示还在使用的机器。   那么我们每次把已经使用完的加入q,如果关闭了或者被新的人使用了就移除。 使用的新机器加入q1....

    #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 = 3e5 + 10;
    struct node
    {
        ll a, s;
        bool operator < (const node& b)const{
            return a < b.a || a == b.a && s < b.s;
        }
    }a[maxn];
    int ans; ll n, m;
    queue<int>q;
    priority_queue<int>q1;
    int main()
    {
        scanf("%lld%lld", &n, &m);
        for(int i = 1; i <= n; i++)
            scanf("%lld%lld", &a[i].a, &a[i].s);
        sort(a + 1, a + 1 + n);
        for(int i = 1; i <= n; i++)
        {
            while(!q1.empty())
                if(-q1.top() <= a[i].a)q.push(-q1.top()+m),q1.pop();
                else break;
            while(!q.empty()&&q.front()<a[i].a)q.pop();
            if(!q.empty())q1.push(-a[i].a-a[i].s),q.pop();
            else ans++,q1.push(-a[i].a-a[i].s);
        }
        printf("%d",n-ans);
        return 0;
    }
    View Code

    B .Better Productivity

    题意:给出N个人,现在让你分P组,每组的工作效率是最小结束时间-最大开始时间,要求每一组的效率的正数,求最大效率和。

    思路: 把包含至少一个其他的分到A组;否则到B组。  

                A组的要么单独分到一组,要么和它包含的某一个在一组(可以反证,假设已经分好组了,现在把不是单独分组的A加进去,如果分到不是包含关系的里面去,只会把答案变小)。   

               分组可以用栈进行。 而不是N^2枚举,因为多个相同的时候我们可以要保留一个作为最小的一个分到B组。

                然后,现在A里面的没有包含关系了,我们可以排序,排序后一定是相邻的分到同一组,这里DP即可。         

               枚举单独分组的A,加上dp[][]跟新最大值即可。 dp[i][j]表示前i个人分j组的最大效率和; mp[i][j]表示i到j的人分一组的效率。

    (这种题一看就是排序,毕竟交集肯定是相邻的在一起最大,然后DP或者线段树啥的。但是这里按照左端点和右端点呢,显然在有包含关系的时候都不行,所以要去掉包含关系,比赛的时候想到这里了,然后就没思路了。 关键在于想到A组的人如果不单独分组的话,他可以分到合理的组,使得答案不减小。

    #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;
    const int maxn=210;
    pii a[maxn];int tot,cnt,ans;
    int vis[maxn],dp[maxn][maxn],len[maxn],mp[maxn][maxn];
    int main()
    {
        int N,P;
        scanf("%d%d",&N,&P);
        rep(i,1,N) scanf("%d%d",&a[i].first,&a[i].second);
        sort(a+1,a+N+1);
        rep(i,1,N)
         rep(j,i+1,N)
           if(a[i].second>=a[j].second){ vis[i]=1; break;}
        rep(i,1,N)
          if(vis[i]) len[++tot]=a[i].second-a[i].first;
          else a[++cnt]=a[i];
        sort(len+1,len+tot+1); reverse(len+1,len+tot+1);
        rep(i,1,tot) len[i]+=len[i-1];
    
        rep(i,1,cnt){
           int Mx=a[i].second,Mn=a[i].first;
           rep(j,i,cnt){
              Mx=min(Mx,a[j].second),Mn=max(Mn,a[j].first);
              mp[i][j]=Mx-Mn;
           }
        }
    
        memset(dp,-1,sizeof(dp)); dp[0][0]=0;
        rep(i,1,min(cnt,P))
         rep(j,1,cnt)
          rep(k,0,j-1) {
            if(mp[k+1][j]>0&&dp[k][i-1]!=-1)
             dp[j][i]=max(dp[j][i],dp[k][i-1]+mp[k+1][j]);
        }
    
        rep(i,max(P-cnt,0),tot){
            if(dp[cnt][P-i]>0)
            ans=max(ans,len[i]+dp[cnt][P-i]);
        }
    
        printf("%d
    ",ans);
        return 0;
    }
    View Code

    C .Cleaning Pipes

    #include<bits/stdc++.h>
    #define pii pair<int,int>
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const double eps = 1e-10;
    const double pi = 3.1415926535897 ;
    struct Point
    {
        double x, y;
        Point(double x = 0, double y = 0):x(x), y(y){}
    };
    typedef Point Vector;
    Vector operator + (Vector A, Vector B){return Vector(A.x+B.x, A.y+B.y);}//向量+向量=向量;点+向量=向量
    Vector operator - (Vector A, Vector B){return Vector(A.x-B.x, A.y-B.y);}//点-点=向量
    bool operator < (const Point& a, const Point& b)
    {
        return a.x < b.x || (a.x == b.x && a.y < b.y);
    }
    int dcmp(double x)//三态函数,高精度判断
    {
        if(fabs(x) < eps)return 0; else return x < 0 ? -1 : 1;
    }
    bool operator == (const Point& a, const Point& b)
    {
        return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
    }
    double Cross(Vector A, Vector B){return A.x*B.y - A.y*B.x;}//叉积
    bool SegmentProperIntersection(Point a1, Point a2, Point b1, Point b2)
    //判断线段a1a2和b1b2是否规范相交(在端点处相交得用下一个函数特殊判断)
    {
        double c1 = Cross(a2-a1, b1-a1), c2 = Cross(a2-a1, b2-a1),
               c3 = Cross(b2-b1, a1-b1), c4 = Cross(b2-b1, a2-b1);
        return dcmp(c1) * dcmp(c2) < 0 && dcmp(c3) * dcmp(c4) < 0;
    }
    Point a[1010];
    int c[1010];
    Point b[1010];
    bool judge(int i, int j)//如果i和j相交,且交点不为两个线段的起点
    {
        if(b[i] == b[j])return true;
        if(a[c[i]] == a[c[j]] || a[c[i]] == b[j] || b[i] == a[c[j]])return false;
        return SegmentProperIntersection(a[c[i]],b[i],a[c[j]],b[j]);
    }
    
    int color[1010];//0表示未染色 1表示白色 2表示黑色
    vector<int>G[1010];
    bool bipartite(int u)
    {
        for(int i = 0; i < G[u].size(); i++)
        {
            int v = G[u][i];
            if(color[u] == color[v])return false;//u v颜色一样
            if(!color[v])
            {
                color[v] = 3 - color[u];//节点u与v染不同的颜色
                if(!bipartite(v))return false;
            }
        }
        return true;
    }
    int main(){
        int n, m;
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++)scanf("%lf%lf", &a[i].x, &a[i].y);
        for(int i = 1; i <= m; i++)
            scanf("%d%lf%lf", &c[i], &b[i].x, &b[i].y);
        for(int i = 1; i <= m; i++)
            for(int j = i + 1; j <= m; j++)
                if(judge(i, j))G[i].push_back(j),G[j].push_back(i);
        bool flag = 0;
        for(int i = 1; i <= m; i++)if(!color[i])
        {
            color[i] = 1;
            if(!bipartite(i)){flag = 1;break;}
        }
        if(flag)puts("impossible");
        else puts("possible");
        return 0;
    }
    View Code

    D .Debugging

    题意:题目给N,R,P。表示给出一个一定有一行bug的代码,长度为N,现在让你debug,你可以在某处加一行“输出”,时间是P; 运行一次的时间是R。(输出是表示你可以对比这里的放的答案是否符合预期)。现在稳你用最有策略,最坏情况下的时间。 N<1e6; R,P<1e9

    思路:N很小,所以所有长度的答案都可以算出来,估计就是记忆化了。一开始以为要么是二分,要么用L-1行输出。WA了。然后才去想,还是应该把所有的情况都求出来,长度为L的时候 ,res=min((L+i)/i+R+i*P);我们整数分块即可。

     题目的最坏情况表示:你下一次需要去检验的长度是当前最长的一个区间块,即向上取整,所以我们用(L+i)/i

    #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;
    const int maxn=210;
    pii a[maxn];int tot,cnt,ans;
    int vis[maxn],dp[maxn][maxn],len[maxn],mp[maxn][maxn];
    int main()
    {
        int N,P;
        scanf("%d%d",&N,&P);
        rep(i,1,N) scanf("%d%d",&a[i].first,&a[i].second);
        sort(a+1,a+N+1);
        rep(i,1,N)
         rep(j,i+1,N)
           if(a[i].second>=a[j].second){ vis[i]=1; break;}
        rep(i,1,N)
          if(vis[i]) len[++tot]=a[i].second-a[i].first;
          else a[++cnt]=a[i];
        sort(len+1,len+tot+1); reverse(len+1,len+tot+1);
        rep(i,1,tot) len[i]+=len[i-1];
    
        rep(i,1,cnt){
           int Mx=a[i].second,Mn=a[i].first;
           rep(j,i,cnt){
              Mx=min(Mx,a[j].second),Mn=max(Mn,a[j].first);
              mp[i][j]=Mx-Mn;
           }
        }
    
        memset(dp,-1,sizeof(dp)); dp[0][0]=0;
        rep(i,1,min(cnt,P))
         rep(j,1,cnt)
          rep(k,0,j-1) {
            if(mp[k+1][j]>0&&dp[k][i-1]!=-1)
             dp[j][i]=max(dp[j][i],dp[k][i-1]+mp[k+1][j]);
        }
    
        rep(i,max(P-cnt,0),tot){
            if(dp[cnt][P-i]>0)
            ans=max(ans,len[i]+dp[cnt][P-i]);
        }
    
        printf("%d
    ",ans);
        return 0;
    }
    View Code

    E .Elementary Math

    题意:给你一个数 N, 然后下面有 N 对数,每对数可以进行 + - * 操作,每对数选择一个结果,是不是存在每对数选择的结果都不一样。如果是,把每对数选择的结果写下来,并记录下是什么操作,如果不行,就输出 impossible。
    思路:这个题看着就是一个二分图匹配问题,看能否匹配N对。

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    struct edge{
        int d,nex,dat;
    }e[1000000];
    int n,q[10010],d[10010],efree=1,cnt,ans,fh[2510];
    long long x[2510],y[2510],num[10010];char f[5];
    queue<int>que;
    map<long long,int>ma;
    map<long long,int>::iterator ii;
    inline void add(int x,int y,int z){e[++efree]=(edge){y,q[x],z};q[x]=efree;}
    int dfs(int x,int y){
        if(x==cnt+1||!y)return y;
        int now,tot=0;
        for(int i=q[x];i;i=e[i].nex)
            if(d[e[i].d]==d[x]+1&&e[i].dat){
                now=dfs(e[i].d,min(y,e[i].dat));
                e[i].dat-=now;e[i^1].dat+=now;
                tot+=now;if(tot==y)return y;
            }
        if(!tot)d[x]=-1;
        return tot;
    }
    inline bool bfs(){
        memset(d,0,sizeof(d));d[0]=1;
        que.push(0);
        while(!que.empty()){
            int x=que.front();que.pop();
            for(int i=q[x];i;i=e[i].nex)
                if(e[i].dat&&!d[e[i].d])d[e[i].d]=d[x]+1,que.push(e[i].d);
        }
        return d[cnt+1];
    }
    void dfs(int xx){
        for(int i=q[xx];i;i=e[i].nex)
            if(e[i].dat&&e[i].d<=n){
                if(x[e[i].d]+y[e[i].d]==num[xx])fh[e[i].d]=1;
                else if(x[e[i].d]-y[e[i].d]==num[xx])fh[e[i].d]=2;
                else fh[e[i].d]=3;
                return;
            }
    }
    long long ff(long long x,long long y,int z){
        if(z==1)return x+y;
        if(z==2)return x-y;
        return x*y;
    }
    int main(){
        scanf("%d",&n);cnt=n;f[1]='+';f[2]='-';f[3]='*';
        for(int i=1;i<=n;i++){
            add(0,i,1),add(i,0,0);
            scanf("%lld%lld",x+i,y+i);
            long long a=x[i],b=y[i];
            if(!ma[a+b])ma[a+b]=++cnt,num[cnt]=a+b;
            if(!ma[a-b])ma[a-b]=++cnt,num[cnt]=a-b;
            if(!ma[a*b])ma[a*b]=++cnt,num[cnt]=a*b;
            add(i,ma[a+b],1),add(ma[a+b],i,0);
            add(i,ma[a*b],1),add(ma[a*b],i,0);
            add(i,ma[a-b],1),add(ma[a-b],i,0);
        }
        for(ii=ma.begin();ii!=ma.end();ii++)
            add((*ii).second,cnt+1,1),add(cnt+1,(*ii).second,0);
        while(bfs())ans+=dfs(0,2147483647);
        if(ans==n){
            for(int i=q[cnt+1];i;i=e[i].nex)
                if(e[i].dat)dfs(e[i].d);
            for(int i=1;i<=n;i++)printf("%lld %c %lld = %lld
    ",x[i],f[fh[i]],y[i],ff(x[i],y[i],fh[i]));
        }
        else puts("impossible");
        return 0;
    }
    View Code

    G .Guessing Camels

    题意:给定三个N的排列A,B,C;问多少对数字(x,y),在三个排列里x出现的位置在y的前面

    思路:BC按照A标号,然后CDQ分治即可。(这里可以把排序的部分转化为双指针,会快一个log)

    #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;
    const int maxn=4000010;
    int a[maxn],b[maxn],c[maxn],pos[maxn];
    ll sum[maxn],ans; int N;
    struct in{
        int id,x,y;
    }s[maxn];
    bool cmp1(in w,in v){ return w.id<v.id;}
    bool cmp2(in w,in v){ return w.x<v.x; }
    void add(int x,int val)
    {
        while(x<=N){
            sum[x]+=val;x+=(-x)&x;
        }
    }
    int query(int x){
        int res=0; while(x){
            res+=sum[x]; x-=(-x)&x;
        } return res;
    }
    void solve(int L,int R)
    {
        if(L>=R) return ;
        int Mid=(L+R)>>1;
        solve(L,Mid);
        sort(s+L,s+R+1,cmp2);
        rep(i,L,R){
            if(s[i].id<=Mid) add(s[i].y,1);
            else ans+=query(s[i].y);
        }
         rep(i,L,R){
            if(s[i].id<=Mid) add(s[i].y,-1);
        }
        sort(s+L,s+R+1,cmp1);
        solve(Mid+1,R);
    }
    int main()
    {
        scanf("%d",&N);
        rep(i,1,N) scanf("%d",&a[i]),pos[a[i]]=i;
        rep(i,1,N) scanf("%d",&b[i]),b[i]=pos[b[i]],s[b[i]].x=i;
        rep(i,1,N) scanf("%d",&c[i]),c[i]=pos[c[i]],s[c[i]].y=i;
        rep(i,1,N) s[i].id=i;
        solve(1,N);
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

    I .Identifying Map Tiles

    递推找位置:

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    int dir[][2] = {0,0,1,0,0,1,1,1};
    ll f[40];
    int main()
    {
        string s;
        cin >> s;
        ll n = s.size();
        ll x = dir[s[0] - '0'][0];
        ll y = dir[s[0] - '0'][1];
        x++,y++;
    
        for(int i = 1; i < n; i++)
        {
            x = 2 * x - 1, y = 2 * y - 1;
            if(s[i] == '1')x++;
            else if(s[i] == '2')y++;
            else if(s[i] == '3')x++,y++;
        }
        x--,y--;
        cout<<n<<" "<<x<<" "<<y<<endl;
        return 0;
    }
    View Code

    J .Jumbled Communication

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    int ans[1000];
    
    int main()
    {
        for(int i = 0; i <= 255; i++)ans[((i<<1)&255) ^ i] = i;
        int n;
        scanf("%d", &n);
        for(int i = 0; i < n; i++)
        {
            int x;
            scanf("%d", &x);
            printf("%d", ans[x]);
            if(i == n - 1)puts("");
            else putchar(' ');
        }
        return 0;
    }
    View Code

    K .Kitchen Combinatorics

    题意: 从前菜,主菜,点心中各选一道菜,每道菜由一些原料组成,每种原料有一些不同的品种,问总共有多少种方法组成这三道菜。如果多道菜使用了相同的原料,则这些菜中的这种原料将是相同品种的,另外有一些不可以组合在一起的菜。
    思路: 枚举所有可能组合,统计该组合使用不同原料的数目,将原料品种数相乘即为该组合方案数。相乘前先要用除法判断是否超过1e18

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    int r,s,m,d,n,k[100],b[1010],a[100][25];
    bool f[100][100],fl[1010];long long ans;
    int main(){
        scanf("%d%d%d%d%d",&r,&s,&m,&d,&n);
        for(int i=1;i<=r;i++)scanf("%d",b+i);
        for(int i=1;i<=s+m+d;i++){
            scanf("%d",k+i);
            for(int j=1;j<=k[i];j++)scanf("%d",a[i]+j);
        }
        for(int i=1;i<=n;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            f[x][y]=f[y][x]=1;
        }
        for(int i=1;i<=s;i++)
            for(int j=1;j<=m;j++)
                if(!f[i][s+j])
                    for(int l=1;l<=d;l++)
                        if(!f[i][l+s+m]&&!f[s+j][l+s+m]){
                            memset(fl,0,sizeof(fl));
                            for(int p=1;p<=k[i];p++)fl[a[i][p]]=1;
                            for(int p=1;p<=k[j+s];p++)fl[a[j+s][p]]=1;
                            for(int p=1;p<=k[l+s+m];p++)fl[a[l+s+m][p]]=1;
                            long long now=1;bool flag=0;
                            for(int p=1;p<=r;p++)
                                if(fl[p]){
                                    if(now>1e18/b[p])return 0*puts("too many");
                                    now*=b[p];
                                }
                            if(ans+now>1e18)return 0*puts("too many");
                            ans+=now;
                        }
        printf("%lld",ans);
        return 0;
    }
    View Code
  • 相关阅读:
    初级知识点四——设计模式六大原则
    初级知识点三——面向对象的三大特性
    初级知识点二——C#值传递
    初级知识点一——C#中的值类型与引用类型
    Unity资源引用问题
    Git在windows上的设置详解
    Unity中接收服务器消息并广播事件的实现
    Sphinx 自动化文档
    MAC Pro 同时安装 Python2 和 Python3
    数据中心网络架构演进 — 云网融合
  • 原文地址:https://www.cnblogs.com/hua-dong/p/10525566.html
Copyright © 2011-2022 走看看