Nordic Collegiate Programming Contest 2016

A Artwork

输入:

n,m表示原图为n*m个白色方格,输入x1,y1,x2,y2表示将x1,y1,x2,y2涂为黑色。

输出:

对于每个x1,y1,x2,y2输入当前图案白色联通块的数目。

思路:

先维护得到最后图案的形状,包括每个黑色方块的厚度。

对于每个x1,y1,x2,y2倒序访问,将其对应位置的黑色还原为白色。通过上下左右的联通关系,更新并查集得到每组的答案。

#include<bits/stdc++.h>
using namespace std;
struct sc
{
    int a,b,c,d;
} edg[10010];
int p[1100100],g[1010][1010],ans[10010];
int dx[]= {0,1,-1,0};
int dy[]= {1,0,0,-1};
int n,m,q,cnt=0,k=0;
int f(int x)
{
    return x==p[x]?x:p[x]=f(p[x]);
}
void u(int a,int b)
{
    int x=f(a),y=f(b);
    p[x]=y;
}
int id(int x,int y)
{
    return x*(m+2)+y+1;
}
int main()
{
    scanf("%d %d %d",&n,&m,&q);
    memset(g,0,sizeof g);
    for(int i=0; i<=n+1; i++)
        for(int j=0; j<=m+1; j++)
        {
            p[id(i,j)]=id(i,j);
            if(!i||!j|i>n||j>m)g[i][j]=1;
        }
    for(int i=0; i<q; i++)
    {
        int a,b,c,d;
        scanf("%d %d %d %d",&a,&b,&c,&d);
        for(int k=a; k<=c; k++)
            for(int j=b; j<=d; j++)
                g[k][j]++;
        edg[i]= {a,b,c,d};
    }
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++)
            if(!g[i][j])
                for(int l=0; l<4; l++)
                    if(!g[i+dx[l]][j+dy[l]])
                        u(id(i+dx[l],j+dy[l]),id(i,j));
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++)
            if(id(i,j)==f(id(i,j))&&!g[i][j])
                cnt++;

                
    while(q--)
    {
        int a=edg[q].a,b=edg[q].b,c=edg[q].c,d=edg[q].d;
        ans[k++]=cnt;
        for(int i=a; i<=c; i++)
            for(int j=b; j<=d; j++)
            {
                g[i][j]--;
                if(!g[i][j])
                {
                    cnt++;
                    for(int l=0; l<4; l++)
                    {
                        int x=i+dx[l],y=j+dy[l];
                        if(!g[x][y]&&f(id(x,y))!=f(id(i,j)))
                        {
                            u(id(i,j),id(x,y));
                            cnt--;
                        }
                    }
                }
            }
    }
    for(int i=k-1; i>=0; i--)printf("%d\n",ans[i]);
}
View Code

反思:

场上心里闪过并查集的念头,但是只想到了对于黑色进行并查集,没有从一个角度看问题,比如反过来看,看答案的对应面,比如补集,或者倒着获得答案。

数组开小了, 应该算清楚每个数组的范围。

扫描二维码关注公众号,回复: 3610894 查看本文章

B Bless You Autocorrect!

输入:

n,m表示下面有n,m个串,n表示手机里的字典词按照出现的顺序表示优先级,m表示你想输入的单词。

输出:

对于每个你想输入的单词,输出最小打出它需要的按键次数。

思路:

用字典树来存储单词,字典树(树上的每一个串的子串都会有一个id),通过这些id和每个串每打一个按键可以得到的另外一个串建立双向边。

跑一遍bfs即可获得每个串可以打出需要的最小按键数量。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6;
char s[maxn];
int tot=0,tree[30][maxn],edg=0,head[maxn],dp[maxn],cnt[maxn],vis[maxn];
struct sc
{
    int to,nxt;
}p[6*maxn];
void add(int a,int b)
{
    p[edg]={b,head[a]},head[a]=edg++;
}
void inser()
{
    int l=strlen(s),nxt=0;
    for(int i=0;i<l;i++)
    {
        int id=s[i]-'a';
        if(!tree[id][nxt])
        tree[id][nxt]=++tot;
        add(nxt,tree[id][nxt]);
        add(tree[id][nxt],nxt);
        nxt=tree[id][nxt];
        cnt[nxt]++;
    }
    int en=nxt;
    nxt=0;
    for(int i=0;i<l-1;i++)
    {
        int id=s[i]-'a';
        if(cnt[tree[id][nxt]]==1)add(tree[id][nxt],en);
        nxt=tree[id][nxt];
    }
}
void bfs()
{
    memset(dp,0x3f,sizeof dp);
    dp[0]=0;
    vis[0]=1;
    queue<int>q;
    for(q.push(0);!q.empty();q.pop())
    {
        int v=q.front();
        for(int i=head[v];~i;i=p[i].nxt)
        {
            int u=p[i].to;
            if(!vis[u])
            {
                vis[u]++;
                dp[u]=dp[v]+1;
                q.push(u);
            }
        }
    }
}
int ans()
{
    int l=strlen(s),mi=l,nxt=0,u;
    for(int i=0;i<l;i++)
        {
            int id=s[i]-'a';
            if(tree[id][nxt])mi=min(dp[tree[id][nxt]]+l-1-i,mi);
            else break;
            nxt=tree[id][nxt];
        }
    return mi;
}
int main()
{
    //freopen("i.txt","r",stdin);
    //freopen("o.txt","w",stdin);
    memset(tree,0,sizeof tree);
    memset(head,-1,sizeof head);
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i=0;i<n;i++)
    {
        scanf("%s",s);
        inser();
    }
    bfs();
    for(int i=0;i<m;i++)
    {
        scanf("%s",s);
        printf("%d\n",ans());
    }
}
View Code

反思:

这道题对于每个状态的互相转移关系,可以看作类似最短路,需要一点点思维吧(是不是可以用map映射id来做这道题呢)。

C Card Hand Sorting

输入:

扑克牌的花色和点数。

输出:

相同花色放在一起且牌成递增或者递减,最小需要的操作数。

思路:

扑克牌最后形成的结果只有1<<4*4的全排列中,枚举之

#include<bits/stdc++.h>
using namespace std;
pair<int,int>s[200];
int spoke[5][20],cnt[5],vis[5],p[5],n,lis[60],ans=0x3f3f3f3f;
map<char,int>m;
map<pair<int,int>,int>mm;
void init()
{
    m['s']=0,m['h']=1,m['d']=2,m['c']=3;
    m['T']=10,m['J']=11,m['Q']=12,m['K']=13,m['A']=14;
    memset(spoke,0,sizeof spoke);
    memset(cnt,0,sizeof cnt);
    memset(vis,0,sizeof vis);
    for(int i=2; i<=9; i++)m['0'+i]=i;
}
bool cmp(int a,int b)
{
    return a>b;
}
void dfs(int inde)
{
    if(inde==4)
    {
        for(int i=0;i<16;i++)
        {
            for(int j=0;j<4;j++)sort(spoke[j],spoke[j]+cnt[j]);
            for(int j=0;j<4;j++)if((1<<j)&i)sort(spoke[j],spoke[j]+cnt[j],cmp);
            mm.clear();
            int k=0,ll=0,haha=0;
            for(int j=0;j<4;j++)
            {
                int tem=p[j];
                for(int l=0;l<cnt[tem];l++)
                    mm[{spoke[tem][l],tem}]=k++;
            }
            memset(lis,0x3f,sizeof lis);
            for(int j=0;j<n;j++)
            {
                ll=lower_bound(lis,lis+haha,mm[s[j]])-lis;
                haha=max(ll+1,haha);
                lis[ll]=mm[s[j]];
            }
            ans=min(n-haha,ans);
        }
    }
    for(int i=0;i<4;i++)
    {
        if(!vis[i])
        {
            vis[i]++;
            p[inde]=i;
            dfs(inde+1);
            vis[i]=0;
        }
    }
}
int main()
{
    init();
    scanf("%d ",&n);
    for(int i=0; i<n; i++)
    {
        char a,b;
        scanf("%c%c ",&a,&b);
        int inde=m[b];
        s[i]={m[a],inde};
        spoke[inde][cnt[inde]++]=m[a];
    }
    dfs(0);
    cout<<ans<<endl;
}
View Code

反思:

场上的时候一直在想怎么直接的贪心思路,对于答案情况很少的题可以直接枚举之。

D Daydreaming Stockbroker

 思维,水题

E Exponial

数学,没补

F Fleecing the Raffle

推公式,水题

G Game Rank

模拟,水题

 H Highest Tower

输入:

n个矩形的长宽

输出:

严格递减落成塔形,塔的最高高度

思路:

建树,出为底,入为高,出读最多的为1,(deg-1)是他最为高出现的次数,(deg-1)*h为他对于答案的贡献,如果图案是一个树,那么可以选择一个最大的点作为根节点,得到的答案最大,如果图案是一个图,那么形成的关系是唯一的直接计算即可。

#include<bits/stdc++.h>
using namespace std;
const int axn=550000+10;
struct sc
{
    int to,nxt;
} p[2*axn];
int head[2*axn],edg=0,id=1,deg[axn],fm[axn],vis[axn],maxn;
long long ans=0,degg;
map<int,int>m;
void add(int a,int b)
{
    deg[a]++,deg[b]++;
    p[edg]= {b,head[a]},head[a]=edg++;
    p[edg]= {a,head[b]},head[b]=edg++;
}
void dfs(int u)
{
    if(vis[u])return ;
    vis[u]=1;
    degg+=deg[u]-2;
    maxn=max(maxn,fm[u]);
    ans+=(long long)(deg[u]-1)*fm[u];
    for(int i=head[u];~i;i=p[i].nxt)
            dfs(p[i].to);
}
int main()
{
    memset(vis,0,sizeof vis);
    memset(head,-1,sizeof head);
    //freopen("i.txt","r",stdin);
    //freopen("o.txt","w",stdout);
    int n,x,y;
    scanf("%d",&n);
    for(int i=0; i<n; i++)
    {
        scanf("%d %d",&x,&y);
        if(!m[x])m[x]=id,fm[id++]=x;
        if(!m[y])m[y]=id,fm[id++]=y;
        add(m[x],m[y]);
    }
    for(int i=1;i<id;i++)
    {
        maxn=0,degg=0;
        dfs(i);
        if(degg<0)ans+=maxn;
    }
    cout<<ans<<endl;
}
View Code

反思:

对于涉及矩形长宽的题,思考能否把边看成点建图解决。

I Interception

没补

J Jumbled Compass

思维,水题。

K Keeping the Dogs Apart

蓝书有板子,还没研究。

猜你喜欢

转载自www.cnblogs.com/ncc62497/p/9809962.html
今日推荐