2016-2017 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2016)

Time:2018.4.3   18:00 - 22:00

Link


A Artwork       solved by ym    solved by czh

题意

给一个n×m一个网格,有q个询问,每次询问有两个点坐标,表示从第一个点到第二个点都被标记(这两个点x/ y相同),对于q个询问,每次输出网格共被分成几个区域(1<=n,m<=1e3,q<=10000)

分析

比赛的时候对于每个q直接bfs不出意外的tle的起不来,没有好的思路,死掉

赛后:联通块应该想到并查集来解决

           对询问倒着作,并查集维护联通块即可

           hash函数写反错了一年才找出来  by ym

           赛后学长指点了一下,但写出来后超时,原来是并查集路径压缩错了,以前用的路径压缩都不是很快 by czh

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1000+7;
const int maxm = 1e6+200;
int dx[5]={1,-1,0,0};
int dy[5]={0,0,-1,1};

int n,m,q;
int num[maxn][maxn];
int answer[maxm];
int fa[maxm];
int x1[maxn*10], y1[maxn*10], x2[maxn*10], y2[maxn*10];

void init()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            fa[(i-1)*m+j]=(i-1)*m+j;
        }
    }
}

int findfa(int x)
{
    if(x==fa[x])
    return x;
    else
    return  fa[x]=findfa(fa[x]);
}
int tot=0;
void join(int x,int y)
{
    int xx=findfa(x);
    int yy=findfa(y);
    if(xx!=yy)
    {
        fa[yy]=xx;
        tot--;
    }
}


void ok(int x,int y)
{
    for(int i=0;i<4;i++)
    {
        int xx=x+dx[i];
        int yy=y+dy[i];
        if(xx>=1 && xx<=n && yy>=1 && yy<=m && num[xx][yy]==0)
        {
            join((xx-1)*(m)+yy, (x-1)*m+y);
        }
    }
}

int main()
{
    scanf("%d%d%d", &n, &m, &q);
    init();
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d%d%d", &x1[i], &y1[i], &x2[i], &y2[i]);
        if(x1[i]==x2[i])
        {
            for(int j=y1[i];j<=y2[i];j++)
            {
                num[x1[i]][j]++;
            }
        }
        else
        {
            for(int j=x1[i];j<=x2[i];j++)
            {
                num[j][y1[i]]++;
            }
        }
    }
    
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(num[i][j]==0)
            {
                ok(i,j);
                tot++;
            }
        }
    }

    for(int i=q;i>=1;i--)
    {
        answer[i]=tot;
        if(x1[i]==x2[i])
        {
            for(int j=y1[i];j<=y2[i];j++)
                {
                --num[x1[i]][j];
                if(num[x1[i]][j]==0)
                {
                    tot++;
                    ok(x1[i],j);
                }
            }
        }
        else
        {
            for(int j=x1[i];j<=x2[i];j++)
            {
                --num[j][y1[i]];
                if(num[j][y1[i]]==0)
                {
                    tot++;
                    ok(j,y1[i]);
                }
            }
        }
    }
    for(int i=1;i<=q;i++) printf("%d
", answer[i]);
    return 0;
}

B  Bless You Autocorrect!        solved by none

题意

给一本有n个单词的词库具有自动补全功能,优先级从高到低,q个询问,问每个询问的单词的最小输入(1<=n<=1e5,1<=q<=1e5)

注意:不存在一个单词由多个单词共同构成

分析

将所有单词构成一颗字典树,根据优先级建好边后,在树上跑个tree dp

目前码不出来,留坑

zls代码

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

struct Node
{
    int nxt[26];
    int jump;
    int fa;
};

Node trie[2000005];
int n,m;
string s[100005];
int en[100005];
int dist[2000005];
queue<int> q;
int num=0;

int insert(string s)
{
    int now=0,dq;
    for (int i=0;i<s.size();++i)
    {
        int dq=s[i]-'a';
        if (trie[now].nxt[dq]==-1)
        {
            ++num;trie[now].nxt[dq]=num;
            trie[num].fa=now;
            for (int j=0;j<26;++j) trie[num].nxt[j]=-1;
            trie[num].jump=-1;
            add(now,num,1);
            add(num,now,1);
        }
        now=trie[now].nxt[dq];
    }
    return now;
}
void addedge(string s,int en)
{
    int now=0,dq;
    for (int i=0;i<s.size();++i)
    {
        int dq=s[i]-'a';
        if (trie[now].jump==-1)
        {
            add(now,en,1);
            trie[now].jump=en;
        }
        now=trie[now].nxt[dq];
    }
    if (trie[now].jump==-1)
    {
        trie[now].jump=en;        
        add(now,en,1);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i) cin>>s[i];
    for (int i=0;i<26;++i) trie[0].nxt[i]=-1;
    trie[0].jump=0,trie[0].fa=-1;
    for (int i=1;i<=n;++i)
    {
        int tmp=insert(s[i]);
        addedge(s[i],tmp);
    }
    for (int i=1;i<=m;++i)
    {
        cin>>s[0];
        en[i]=insert(s[0]);
    }
    for (int i=0;i<=num;++i) dist[i]=1e9;
    dist[0]=0;
    while (!q.empty()) q.pop();
    q.push(0);
    trie[0].jump=-1;
    while (!q.empty())
    {
        int x=q.front();q.pop();
        for (int i=0;i<26;++i) if (trie[x].nxt[i]!=-1)
        {
            int y=trie[x].nxt[i];
            if (dist[y]!=1e9) continue;
            dist[y]=dist[x]+1;
            q.push(y);
        }
        if (trie[x].fa!=-1)
        {
            int y=trie[x].fa;
            if (dist[y]==1e9){
            dist[y]=dist[x]+1;
            q.push(y);}
        }
        if (trie[x].jump!=-1)
        {
            int y=trie[x].jump;

            if (dist[y]==1e9) {
            dist[y]=dist[x]+1;
            q.push(y);}
        }
    }
    for (int i=1;i<=m;++i) printf("%d
",dist[en[i]]);
    return 0;
}

C Card Hand Sorting         solved by czh

题意

有n张牌排成一行,每张牌两个属性,第一个数字/字母代表它在这一类的rank,后一个字母代表它的种类,共有13个rank和4种牌,现要求同种牌必须放在相邻位置,每种牌内部是有序的,问最少需要移动几次(移动是任意的)(n<=52)

分析

ym:暴力枚举+maskdp,待补

czh:注意到n最大为52,枚举花色的顺序和花色内的顺序,然后求最大子序列

#include<bits/stdc++.h>
using namespace std;

map<char,int>mp;
char color[60],a[3];
int pe[4]={1,2,3,4},ind[60],n,num[60];
map<char,int>ma;
int dp[60];

int LIS()
{
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++)
        dp[i]=1;
    int maxx=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<i;j++)
        {
            if(num[i]>num[j])
                dp[i]=max(dp[i],dp[j]+1);
        }
        maxx=max(maxx,dp[i]);
    }
    return maxx;
}

int main()
{
    ma['s']=0;ma['h']=1;ma['d']=2;ma['c']=3;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",a);
        if(a[0]>='0'&&a[0]<='9')
            ind[i]=a[0]-'0';
        else if(a[0]=='T')ind[i]=10;
        else if(a[0]=='J')ind[i]=11;
        else if(a[0]=='Q')ind[i]=12;
        else if(a[0]=='K')ind[i]=13;
        else if(a[0]=='A')ind[i]=14;
        color[i]=a[1];
    }
    int ans=0;
    do{
        for(int i=0;i<16;i++)
        {
            for(int j=1;j<=n;j++)
            {
                num[j]=pe[ma[color[j]]]*1000;
                if(((1<<ma[color[j]])&i)==0)
                    num[j]-=ind[j];
                else
                    num[j]+=ind[j];
            }
            ans=max(ans,LIS());
        }
    }while(next_permutation(pe,pe+4));
    cout<<n-ans<<endl;
} 

D Daydreaming Stockbroker    solved by ym   solved by czh

题意

给你n天的物价,初始有100元,库存量最大不能超过1e5,问最后做多可以有多少钱

对比:http://codeforces.com/contest/867/problem/E

分析

此题感觉能挣钱就买卖,低进高出即可

CF867E,一天只能买或者卖

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int a[370];

int main()
{
    int n;
    scanf("%d", &n);
    for(int i=1;i<=n;i++)  scanf("%d", &a[i]);
    ll sum=100;
    for(int i=1;i<n;i++)
    {
        if(a[i+1]>a[i])
        {
            ll num=min(sum/a[i], 100000LL);
            sum=sum-num*a[i]+num*a[i+1];
        }
    }
    printf("%lld
", sum);
    return 0;
}

E Exponial

数论题


F Fleecing the Raffle    solved by czh&ym

题意

现在有n个人抽奖,共有p个奖,现在可以将任意数量自己的名字放入箱子里,问自己最大的中奖概率

分析

设添加x个自己,则中奖概率为 C(x,1) * C(n,p-1) / C(n+x,p)

组合式推一下,化简一下不难看出规律,暴力找最大值即可


G Game Rank  solved by czh   solved by ym

题意

模拟题

分析

ym:按题意模拟即可

czh:题目很绕,没有很快理解出题意 by czh


H Highest Tower

DIfficult


I Interception

DIfficult


J Jumbled Compass    solved by czh   solved by ym

签到题


K Keeping the Dogs Apart

DIfficult


Summary

Ym:训练的时候很困=_=,没状态,还读错题了,坑了,和学弟一起训练体验很好呀

         读题能力不行,需要及时背单词,提升读题能力

         提升思维能力

Czh:  英语水平要求很高,一两个关键单词认错往往这题就过不了

原文地址:https://www.cnblogs.com/Deadline/p/8709047.html