Nordic Collegiate Programming Contest 2016题解

Nordic Collegiate Programming Contest 2016

A:
从前往后记录每次操作后变化的点。
然后从后往前维护并查集。

#include<bits/stdc++.h>
using namespace std;
const int N = 1000 ,Q = 1e4+7;
int G[N][N],fa[N*N];
int dir[4][2]={ {1,0},{-1,0},{0,1},{0,-1} };
int n,m,q,ans;
vector<int> op[Q];
int id(int x,int y) { return x*m+y; }
int f(int x){ return x==fa[x]?x:fa[x]=f(fa[x]); }
void Union(int x,int y)
{
    x=f(x);
    y=f(y);
    if(x==y) return ;
    --ans;
    fa[x]=y;
}
bool check(int x,int y)
{
    if(x<0||y<0||x>=n||y>=m) return false;
    if(G[x][y]) return false;
    return true;
}
int main()
{
    scanf("%d%d%d",&n,&m,&q);
    ans=n*m;
    for(int i=0;i<q;++i)
    {
        int x1,x2,y1,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        --x1;--y1;--x2;--y2;
        if(x1==x2)
        {
            for(int j=y1;j<=y2;++j)
                if(!G[x1][j])
                {
                    --ans;
                    G[x1][j]=1;
                    op[i].push_back(id(x1,j));
                }
        }
        else
        {
            for(int j=x1;j<=x2;++j)
                if(!G[j][y1])
                {
                    --ans;
                    G[j][y1]=1;
                    op[i].push_back(id(j,y1));
                }
        }
    }
    int L = n*m;
    for(int i=0;i<L;++i) fa[i]=i;
    for(int i=0;i<n;++i)
        for(int j=0;j<m;++j)
            if(!G[i][j])
            for(int k=0;k<4;++k)
            {
                int x=i+dir[k][0];
                int y=j+dir[k][1];
                if(check(x,y)) Union(id(i,j),id(x,y));
            }
    vector<int> Ans;
    for(int i=q-1;i>=0;--i)
    {
        Ans.push_back(ans);
        for(int k : op[i])
        {
            int x=k/m;
            int y=k%m;
            G[x][y]=0;
            ++ans;
            for(int j=0;j<4;++j)
            {
                int tx=dir[j][0]+x;
                int ty=dir[j][1]+y;
                if(check(tx,ty)) Union(id(x,y),id(tx,ty));
            }
        }
    }
    reverse(Ans.begin(),Ans.end());
    for(int k : Ans) printf("%d\n",k);
    return 0;
}

B:
只有三种操作:在末尾添加一个字符,去除末尾的一个字符,自动补全。
将所有的字典串放在Trie上,每种操作可以描绘Trie上的一条边。
然后做一次bfs。
每次查询沿着Trie查询到LCP,答案等于最后那个点距离根的距离再加上串长-LCP长。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+7;
int d[N],vis[N];
struct Trie
{
    int nxt[N][26],L,root,fail[N],s[N],top;
    vector<int> adj[N];
    int newnode()
    {
        for(int i=0;i<26;++i)
            nxt[L][i]=-1;
        return L++;
    }
    void init()
    {
        L=0;
        root=newnode();
    }
    void addedge(int u,int v)
    {
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    void insert(char buf[],int len)
    {
        int now=root;top=0;
        for(int i=0;i<len;++i)
        {
            if(nxt[now][buf[i]-'a']==-1)
            {
                nxt[now][buf[i]-'a'] = newnode();
                s[top++]=nxt[now][buf[i]-'a'];
            }
            addedge(now,nxt[now][buf[i]-'a']);
            now=nxt[now][buf[i]-'a'];
        }
        int des=now;
        for(int i=0;i<top;++i) adj[s[i]].push_back(des);
    }
    void bfs()
    {
        queue<int> q;
        d[0]=0;
        vis[0]=1;
        q.push(0);
        while(!q.empty())
        {
            int u=q.front();q.pop();
            vis[u]=true;
            for(int v : adj[u])
            {
                if(!vis[v])
                {
                    vis[v]=1;
                    d[v]=d[u]+1;
                    q.push(v);
                }
            }
        }
    }
    int query(char buf[],int len)
    {
        int now=root;
        for(int i=0;i<len;++i)
        {
            if(nxt[now][buf[i]-'a']==-1) return d[now]+len-i;
            now=nxt[now][buf[i]-'a'];
        }
        return d[now];
    }
}t;
int n,m;
char str[N];
int main()
{
    scanf("%d%d",&n,&m);
    t.init();
    for(int i=0;i<n;++i)
    {
        scanf("%s",str);
        int len=strlen(str);
        t.insert(str,len);
    }
    t.bfs();
    for(int i=0;i<m;++i)
    {
        scanf("%s",str);
        int len=strlen(str);
        printf("%d\n",t.query(str,len));
    }
    return 0;
}

C:
根据花色和点数的要求枚举每种排序结果写出<运算符,一共有 4!×24 种。
答案等于 nLIS

#include<bits/stdc++.h>
using namespace std;
int fuck[128],gg[128],p[4],s,n;
struct Node
{
    int c,x;
    Node(){}
    Node(int c,int x):c(c),x(x){}
    bool operator < (const Node& b) const
    {
        if(c!=b.c) return p[c]<p[b.c];
        if((s>>c)&1) return x<b.x;
        return x>b.x;
    }
}ipt[52],dp[52];
int LIS(Node* arr, int len){
    int k=0;
    for(int i=0;i<len;++i){
        int idx=upper_bound(dp,dp+k,arr[i])-dp;
        dp[idx]=arr[i];
        if(idx==k)++k;
    }
    return k;
}
int main()
{
    scanf("%d",&n);
    for(char i='2';i<='9';++i) fuck[i]=i-'2';
    fuck['T']=8;
    fuck['J']=9;
    fuck['Q']=10;
    fuck['K']=11;
    fuck['A']=12;
    gg['s']=0;
    gg['h']=1;
    gg['d']=2;
    gg['c']=3;
    for(int i=0;i<n;++i)
    {
        char s[10];
        scanf("%s",s);
        ipt[i].c=gg[s[1]];
        ipt[i].x=fuck[s[0]];
    }
    for(int i=0;i<4;++i) p[i]=i;
    int ans = 52 ;
    while(1)
    {
        for(s=0;s<16;++s)
            ans=min(ans,n-LIS(ipt,n));
        if(!next_permutation(p,p+4)) break;
    }
    printf("%d\n",ans);
    return 0;
}

H:
因为要求严格递减,所以每种数字仅能在水平长度中出现一次。
对于长为 u ,宽为 v 的矩形, (u,v) 描绘图上的一条边,那么每个结点的出度为 1 <script type="math/tex" id="MathJax-Element-654">1</script> 用来表示水平长度出现一次。这样矩形才是合法的。
这样其他的度数都给垂直长度做贡献。

对于我们描绘出的这个无向图,它的每一个连通块要么是一棵树,要么是一棵树+一条边。
如果是一棵树+一条边,可以形成一条环。
假设另一个图,一开始只有一条环,那么将这个环描述成答案中一层一层的矩形,那么一定是连续的,每当在这个图中加入一个点和一条边,那么新增的矩形所处的位置是确定的,画一下就知道肯定不能仅加入一条边。

对于仅有一棵树的情况,可以允许一个点没有出度,因此我们允许点权最大的那个点没有出度,这样可以最大地加到答案上。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 250007;
int x[N],y[N],p[N*2],notree;
vector<int> adj[N*2],pt;
bool vis[N*2];
void dfs(int u,int pre)
{
    vis[u]=true;
    for(int v : adj[u])
    {
        if(v==pre) continue;
        if(!vis[v]||!notree)
        {
            pt.push_back(u);
            pt.push_back(v);
        }
        if(!vis[v]) dfs(v,u);
        else notree=true;
    }
}
int main()
{
    int n,m=0;
    scanf("%d",&n);
    for(int i=0;i<n;++i)
    {
        scanf("%d%d",&x[i],&y[i]);
        p[m++]=x[i];
        p[m++]=y[i];
    }
    sort(p,p+m);
    m=unique(p,p+m)-p;
    for(int i=0;i<n;++i)
    {
        x[i]=lower_bound(p,p+m,x[i])-p;
        y[i]=lower_bound(p,p+m,y[i])-p;
        adj[x[i]].push_back(y[i]);
        adj[y[i]].push_back(x[i]);
    }
    ll ans=0;
    for(int i=0;i<m;++i)
    {
        if(vis[i]) continue;
        notree=false;
        pt.clear();
        dfs(i,-1);
        sort(pt.begin(),pt.end());
        int mx=0;
        for(int j=0;j<pt.size();++j)
        {
            if(pt[j]==pt[j-1]&&j>0) continue;
            int c=1;
            for(int k=j+1;k<pt.size()&&pt[k]==pt[k-1];++k) ++c;
            ans+=(ll)(c-1)*p[pt[j]];
            mx=max(mx,p[pt[j]]);
        }
        if(!notree) ans+=mx;
    }
    printf("%lld\n",ans);
    return 0;
}

K:
蓝书原题。原题是有SPJ的。
这题在计蒜客上没人过的原因是 计蒜客没加SPJ

#include<bits/stdc++.h>
using namespace std;
typedef long double ld;
const ld eps=1e-10;
const ld PI=acos((ld)(-1.0));
struct Point
{
    ld x,y;
    Point(ld x=0,ld 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); }
Vector operator * (Vector a, ld p) { return Vector(a.x*p, a.y*p); }
Vector operator / (Vector a, ld p) { return Vector(a.x/p, a.y/p); }
bool operator < (const Point& a, const Point &b)
{
    return a.x<b.x||(a.x==b.x&&a.y<b.y) ;
}
int dcmp(ld 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;
}
ld operator * (Vector a, Vector b) { return a.x*b.x+a.y*b.y; }
ld operator ^ (Vector a, Vector b) { return a.x*b.y-b.x*a.y; }
ld length(Vector a) { return sqrt(a*a); }

//求P到线段AB的距离,A和B是线段的端点。
ld distanceToSegment(Point P, Point A, Point B)
{
    if(A==B) return length(P-A);
    Vector v1=B-A,v2=P-A,v3=P-B;
    if(dcmp(v1*v2)<0) return length(v2);
    else if(dcmp(v1*v3)>0) return length(v3);
    else return fabs(v1^v2) / length(v1);
}

const int N = 1e5+7;
int T,n,m;
Point pa[N],pb[N];
ld MIN;
Point read_point()
{
    int x,y;
    scanf("%d%d",&x,&y);
    return Point(x,y);
}
void update(Point P, Point A, Point B)
{
    MIN=min(MIN,distanceToSegment(P,A,B));
}
int main()
{
    MIN=1e8;
    scanf("%d",&n);
    for(int i=0;i<n;++i) pa[i]=read_point();
    scanf("%d",&m);
    for(int i=0;i<m;++i) pb[i]=read_point();
    ld LA=0,LB=0;
    for(int i=1;i<n;++i) LA+=length(pa[i]-pa[i-1]);
    for(int i=1;i<m;++i) LB+=length(pb[i]-pb[i-1]);
    int sa=0,sb=0;
    Point PA=pa[0],PB=pb[0];
    ld v=LA;
    while(sa<n-1&&sb<m-1)
    {
        ld la=length(pa[sa+1]-PA);
        ld lb=length(pb[sb+1]-PB);
        ld T=min(la/v,lb/v);
        Vector va=(pa[sa+1]-PA)/la*T*v;
        Vector vb=(pb[sb+1]-PB)/lb*T*v;
        update(PA,PB,PB+vb-va);
        PA=PA+va;
        PB=PB+vb;
        if(PA==pa[sa+1]) ++sa;
        if(PB==pb[sb+1]) ++sb;
    }
    printf("%.15f\n",(double)MIN);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值