2016 东北四省赛 不科学の题解

原创 2016年10月07日 17:00:39

10.6 hdu有重现赛,我们两个人打,ACE当然很快就过了。H我想的方向有点不对,我一直在推NAND(与非)的性质,比如说交换律啊结合律啊。。。然后队友突然就想到了,但她不会写
我写和调估计用了一个小时还WA了一次。。。Q巨半小时不到1Y。。。OTZ
过了四题以后不知道做哪个。。。我看F过了好多人,我以为就是很简单的记子树size然后每次询问每个点更新到root,就跟队友说我自己写。然后她就去看D,问了问没来的队友,远程一眼秒了,就是离散化以后搜索一下。。。但是我们三个都感觉写不出来就直接没搞。。。
F写了挺久的,然后TLE了。。。后面就知道没戏了。。。直接去查题解改了改。。。

Minimum’s Revenge

模拟一下 kruskal 算法的过程可以发现,取的一定都是 (1,i)
比如: (2,3) 这条边权值为 6 但是 2 和 3 在之前就已经被加进最小生成树里面了。
所以答案即为 ni=2i,求和就是 (n+2)(n1)2

Mr. Frog’s Problem

CD+DC=1t+t

DC=t1 DC=t1 

AC,DB t=BA or AB 

Coconuts

题目中所给的 r 和 c 都非常大,但是坏点的数量 n 却非常小,所以应该想到用离散化【然而我又没有ˊ_>ˋ
据说网格图的离散化至少有三种。。。于是我挑了一种看起来比较优雅的学了学。。。
考虑一张网格图中间有一个坏点,坐标为 (X,Y) ,则图在 x 这一维被分为三个部分
如果用半开区间表示:[1,X),[X,X+1),[X+1,r+1)
可以发现只要用当前区间的右端点减去上一个区间的右端点就能得到当前区间的长度
这个做法就是基于这种思想的,代码(这次加了很多注释):

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <string>
#include <algorithm>

#define lson rt,l,m
#define rson m+1,r
using namespace std;
typedef long long ll;
const int N=205;
const int dx[]={1,0,0,-1};
const int dy[]={0,1,-1,0};
int mp[N<<1][N<<1],vis[N<<1][N<<1];
int x[N],y[N],vx[N<<1],vy[N<<1];
int T,n,r,c;

ll dfs(int x,int y)
{
    if (vis[x][y]||mp[x][y]) return 0;
    vis[x][y]=1;
    //右端点相减得到长度,相乘即为格子数 
    ll res=1LL*(vx[x]-vx[x-1])*(vy[y]-vy[y-1]);
    for (int i=0;i<4;i++) {
        int xx=x+dx[i],yy=y+dy[i];
        if (xx<1||yy<1||xx>r||yy>c) continue;
        res+=dfs(xx,yy);
    }
    return res;
}

int main()
{
    scanf("%d",&T);
    for (int t=1;t<=T;t++) {
        scanf("%d%d%d",&r,&c,&n);
        int cntx=0,cnty=0;
        for (int i=1;i<=n;i++) {
            scanf("%d%d",&x[i],&y[i]);
            vx[cntx++]=x[i];
            vx[cntx++]=x[i]+1;
            vy[cnty++]=y[i];
            vy[cnty++]=y[i]+1;
        }
        /****************************
        **插入边界,保证最左边的坏点左边的格子会被统计,右上下同理
        ****************************/
        vx[cntx++]=1;
        vx[cntx++]=2;
        vx[cntx++]=r;
        vx[cntx++]=r+1;
        vy[cnty++]=1;
        vy[cnty++]=2;
        vy[cnty++]=c;
        vy[cnty++]=c+1;
        /****************************/
        sort(vx,vx+cntx);
        sort(vy,vy+cnty);
        cntx=unique(vx,vx+cntx)-vx;
        cnty=unique(vy,vy+cnty)-vy;
        memset(mp,0,sizeof(mp));
        for (int i=1;i<=n;i++) {
            //离散化并标记坏点 
            int ix=lower_bound(vx,vx+cntx,x[i])-vx+1;
            int iy=lower_bound(vy,vy+cnty,y[i])-vy+1;
            //此时vx[ix]-vx[ix-1]=1
            mp[ix][iy]=1;
        }
        //origin_r=r
        r=lower_bound(vx,vx+cntx,r)-vx+1;
        c=lower_bound(vy,vy+cnty,c)-vy+1;
        //此时vx[0]=1,vx[r]=origin_r+1
        //很巧妙的处理,vy同理 
        memset(vis,0,sizeof(vis));
        vector<ll> ans;
        for (int i=1;i<=r;i++)
            for (int j=1;j<=c;j++) {
                ll res=dfs(i,j);
                if (res) ans.push_back(res);
            }
        sort(ans.begin(),ans.end());
        int tot=ans.size();
        printf("Case #%d:\n%d\n",t,tot);
        for (int i=0;i<tot;i++)
            printf("%lld%c",ans[i],i==tot-1?'\n':' ');
    }
    return 0;
}

Mr. Frog’s Game

玩过连连看的都知道吧。。。就是判断一下周围每条边上有没有相等的,再判断一下有没有相邻的相等的。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <string>
#include <algorithm>

#define lson rt,l,m
#define rson m+1,r
using namespace std;

int T,n,m;
int a[40][40];

bool row(int i)
{
    for (int j=1;j<=m;j++)
        for (int k=j+1;k<=m;k++)
            if (a[i][j]==a[i][k]) return true;
    return false;
}

bool col(int j)
{
    for (int i=1;i<=n;i++)
        for (int k=i+1;k<=n;k++)
            if (a[i][j]==a[k][j]) return true;
    return false;
}

bool adj(int n,int m)
{
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++) {
            if (i>1&&a[i][j]==a[i-1][j]) return true;
            if (i<n&&a[i][j]==a[i+1][j]) return true;
            if (j>1&&a[i][j]==a[i][j-1]) return true;
            if (j<m&&a[i][j]==a[i][j+1]) return true;
        }
    return false;
}

int main()
{
    scanf("%d",&T);
    for (int t=1;t<=T;t++) {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++)
            for (int j=1;j<=m;j++)
                scanf("%d",&a[i][j]);
        printf("Case #%d: ",t);
        if (row(1)||row(n)) {
            puts("Yes");
            continue;
        }
        if (col(1)||col(m)) {
            puts("Yes");
            continue;
        }
        if (adj(n,m)) {
            puts("Yes");
            continue;
        }
        puts("No");
    }
    return 0;
}

Auxiliary Set

一个被删除的点能否被复原取决于这个点是否存在两个不全为空的子树。
所以读入以后dfs一次求出每个点有多少子树
然后对于每次询问,只需要建出删除那些点的树,递归的计算每个点有多少子树不全为空

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <algorithm>

#define lson rt,l,m
#define rson m+1,r
using namespace std;
const int N=100005;
struct edge {
    int go,next;
} eg[N<<1];
int fat[N],last[N],a[N],sz[N],res[N];
vector<int> G[N];
int tot;

void read(int &x)
{
    char c;
    while((c=getchar())<'0' || c>'9');
    x=c-'0';
    while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}

void adde(int x,int y)
{
    eg[++tot].go=y;
    eg[tot].next=last[x];
    last[x]=tot;
}

void dfs(int x,int fa)
{
    sz[x]=0;
    fat[x]=fa;
    for (int i=last[x];i;i=eg[i].next) {
        int &v=eg[i].go;
        if (v==fa) continue;
        dfs(v,x);
        sz[x]++;
    }
}

int check(int u)
{
    if (res[u]!=-1) return res[u];
    int suv=0,son=0;
    for (int i=0;i<G[u].size();i++) {
        if (check(G[u][i])>=1) suv++;
        son++;
    }
//  sz[u]表示u原来有几个子树
//  son表示u在删除结点组成的树中有几个子树
//  suv表示u在删除结点组成的树中有几个不全为空的子树 
    return res[u]=sz[u]-son+suv; 
}

int main()
{
    int T,n,q;
    read(T);
    for (int t=1;t<=T;t++) {
        read(n);read(q);
        tot=0;
        sz[0]=0;
        memset(last,0,sizeof(last));
        for (int i=1;i<n;i++) {
            int x,y;
            read(x);read(y);
            adde(x,y);
            adde(y,x);
        }
        dfs(1,0);
        printf("Case #%d:\n",t);
        while (q--) {
            int k;
            read(k);
            for (int i=1;i<=k;i++) {
                read(a[i]);
                G[a[i]].clear();
                res[a[i]]=-1;
            }
            for (int i=1;i<=k;i++)
                if (res[fat[a[i]]]==-1)
                    G[fat[a[i]]].push_back(a[i]);
            int ans=n-k;
            for (int i=1;i<=k;i++)
                ans+=(check(a[i])>=2);
            printf("%d\n",ans);
        }
    }
    return 0;
}

Basic Data Structure

观察题目里的运算和结果,可以发现只要有 0 结果就一定是 0 ,所以我们可以保存最后一个 0 的位置
又考虑到这个栈可以 pop 和 reverse ,为了快速获取 pop 或者 reverse 以后最后一个 0 的位置,我们应该保存所有 0 的位置
我是用两个数组模拟两个双端队列,q[] 记录的是栈内的数,ze[] 记录的是所有 0 的位置,k 表示现在的迭代方向
pop 就是操作尾指针,reverse 就是交换一下首尾指针,具体看代码吧:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <string>
#include <algorithm>

#define lson rt,l,m
#define rson m+1,r
using namespace std;
const int N=200005;
int q[N<<1],ze[N<<1];

int main()
{
    int T;
    scanf("%d",&T);
    for (int t=1;t<=T;t++) {
        int n;
        scanf("%d",&n);
        int l=N,r=N,k=1;
        int zel=N,zer=N;
        printf("Case #%d:\n",t);
        for (int i=1;i<=n;i++) {
            char s[20];
            scanf("%s",s);
            if (strcmp(s,"PUSH")==0) {
                int x;
                scanf("%d",&x);
                q[r]=x;
                r+=k;
                if (x==0) {
                    ze[zer]=r;
                    zer+=k;
                }
            } else if (strcmp(s,"POP")==0) {
                if (q[r-k]==0) {
                    zer-=k;
                }
                r-=k;
            } else if (strcmp(s,"REVERSE")==0) {
                int newl=r-k;
                int newr=l-k;
                r=newr;l=newl;
                newl=zer-k;
                newr=zel-k;
                zer=newr;zel=newl;
                k=-k;
            } else {
                if (r-l==0) {
                    puts("Invalid.");
                    continue;
                }
                if (zer-zel==0) {
                    if ((r-l)&1) puts("1");
                    else puts("0");
                } else if (zer-k==zel&&q[r-k]==0) {
                    if ((r-l)&1) puts("0");
                    else puts("1");
                } else {
                    int pos=ze[zel];
                    if (abs(pos-l)&1) puts("1");
                    else puts("0");
                }
            }
        }
    }
    return 0;
}
版权声明:本文为博主原创文章,转载请注明出处 http://blog.csdn.net/summonlight

2016CCPC东北地区大学生程序设计竞赛 - 重现赛 部分题解

【题目链接】http://acm.hdu.edu.cn/search.php?field=problem&key=2016CCPC%B6%AB%B1%B1%B5%D8%C7%F8%B4%F3%D1%A...

2015年东北四省区域赛——B题题解(Matrix)

这题其实比较水,由于相关算法还不太会,硬生生的给水过了,但有一个WA点让我们找了好久。。。。这题大致意思就是有一个矩阵A每一个元素的值都是行标列标之和(Aij= i+j ) 然后又两种操作: M ...
  • tt2767
  • tt2767
  • 2015年05月18日 15:01
  • 604

HDU 5927 Auxiliary Set LCA 2016东北4省赛

Auxiliary Set Time Limit: 9000/4500 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) To...

见识一下尾递归的强大!尾递归怎么会比迭代还快!这不科学

1.性能测试     尾递归求Fibonaci数列,三种方法分别是: (1)普通递归 (2)尾递归 (3)动态规划 第一种重复计算很多,其他两种都能避免重复计算 代码: #i...
  • fall221
  • fall221
  • 2013年06月23日 21:27
  • 10074

Qt为啥从4.8直接就跳到5.3了呢?这不科学吧

http://qt-project.org/downloads Qt 5.3 Select the file according to your operating...

HDU 5926 Mr. Frog’s Game(连连看,暴力)——2016CCPC东北地区大学生程序设计竞赛 - 重现赛

HDU 5926 Mr. Frog’s Game(连连看,暴力)——2016CCPC东北地区大学生程序设计竞赛 - 重现赛...

HDU Mr. Frog’s Game 2016CCPC东北地区大学生程序设计竞赛 - 重现赛

Mr. Frog’s Game Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) ...

(HDU 5929)Basic Data Structure 双端队列+模拟 <2016CCPC东北地区大学生程序设计竞赛 - 重现赛 >

Basic Data Structure Time Limit: 7000/3500 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Ot...

HDU 5927 Auxiliary Set【DFS】(2016CCPC东北赛F题)

题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5927【中文题意】一棵节点数为n的有根数,根节点为1,一开始所有的点都是重点,接下来有q次询...

【HDU5923 2016CCPC东北地区大学生程序设计竞赛 - 重现赛 B】【并查集 暴力 复杂度计算】Prediction 生效若干条链上的所有边条件下的联通块情况

Prediction Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Tot...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:2016 东北四省赛 不科学の题解
举报原因:
原因补充:

(最多只允许输入30个字)