Day3题解

西安铁一中2018暑期集训第二期模拟赛Day3题解

本次题目来自:hdu5714hdu5716hdu5713
T1:拍照
  • 题意:有 n 条船,每条船有四个参数 x , y , z , d ,表示船的头尾坐标,离岸边的距离以及方向 ( d = 1 表示向左, d = 1 表示向右 ) 。现在要在岸边选一个位置以与岸边成 45 的方向拍照,并且可以等待适当的时间。每单位时间船会向自己的前进方向前进 1 格,求最多能拍到多少条完整的船。
  • n 10 4 , 10 6 x < y 10 6 , 1 z 10 6 .
  • 题解:首先,对于一条船,如果它能被完整的观测到,那么必须要满足 y z x + z ,这个很简单。但是船是有两个方向的,为此我们可以先固定一个方向的船不动,然后统计另一个方向的船。如果在 x 位置能观测到的向右的船,在 y 位置观测到的向左的船,且满足 x y ,那么在 [ x , y ] 中间的某个位置一定能够全部观测到。
  • 所以将船按照 y z , x + z 排序,用类似差分的方法从左往右,从右往左扫一遍即可。
#include<bits/stdc++.h>
#define maxn 100005
#define inf (1<<30)
#define mod 1000000007
using namespace std;
typedef long long LL;
int read()
{
    char c=getchar();int f=1,sum=0;
    while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
    return sum*f;
}
int n,tot,T;
struct node{
    int x,val,d;
}a[20005];
bool cmp(node r,node s)
{
    if(r.x!=s.x) return r.x<s.x;
    if(r.val!=s.val) return r.val>s.val;
    return r.d>s.d;
}
int ans[20005],tans;
int main()
{
    T=read();
    for(int cas=1;cas<=T;cas++)
    {
        tot=0;tans=0;
        n=read();
        for(int i=1;i<=n;i++)
        {
            int x=read(),y=read(),z=read(),d=read();
            int l=y-z,r=x+z;
            if(l<=r)
            {
                a[++tot].x=l,a[tot].val=1,a[tot].d=d;
                a[++tot].x=r,a[tot].val=-1,a[tot].d=d;
            }
        }
        sort(a+1,a+1+tot,cmp);
        int ret=0;
        for(int i=tot;i>=1;i--)
        {
            if(a[i].d<0 && a[i].val==-1) ++ret;
            ans[i]=max(ans[i+1],ret);
            if(a[i].d<0 && a[i].val==1) --ret;
        }
        ret=0;
        for(int i=1;i<=tot;i++)
        if(a[i].d>0) 
        {
            if(a[i].val==1) ret++;
            tans=max(tans,ret+ans[i]);
            if(a[i].val==-1) ret--;
        }
        printf("Case #%d:\n%d\n",cas,tans);
    }
    return 0;
}
T2:模式匹配
  • 题意:略
  • 题解:利用shift-and字符匹配算法,用bitset优化即可。shift-and算法的讲解见这里。具体做法就是对n个模式串都建立一个bitset即可。代码也很简单。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int read()
{
    char c=getchar();int f=1,sum=0;
    while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
    return sum*f;
}
int n;
char s[2000005],t[105];
int getid(char x)
{
    if(x>='a' && x<='z') return x-'a';
    else if(x>='A' && x<='Z') return x-'A'+26;
    else if(x>='0' && x<='9') return x-'0'+52;
    else return -1;
}
bitset<505> b[65],d; 
int main()
{
    while(gets(s))
    {
        for(int i=0;i<62;i++) b[i].reset();d.reset();
        n=read();
        for(int i=1;i<=n;i++)
        {
            int len=read();
            scanf("%s",t);
            for(int j=0;j<len;j++)
            b[getid(t[j])][i]=1;
        }
        d[0]=1;
        bool flag=0;
        int m=strlen(s);
        for(int i=0;i<m;i++)
        {
            if(getid(s[i])==-1) d.reset();
            else d=d<<1&b[getid(s[i])];d[0]=1;
            if(d[n]==1) flag=1,printf("%d\n",i-n+2);
        }
        if(!flag) puts("NULL");
        getchar();
    }
    return 0;
}
T3:联通块
  • 题意:有一张 n 个点 m 条边且无重边的无向图,求有多少个边集,使得删掉边集里的边后,图里恰好有 K 个连通块
  • 1 n K 14 , 0 m n ( n + 1 ) / 2
  • 题解:状压dp+枚举子集。
  • d p [ i ] [ s t a t e ] 表示当前选的节点状态是state,并且形成了i个联通块的方案数。
  • 那么状态转移方程为: d p [ i ] [ S ] = S S d p [ i 1 ] [ S S ] d p [ 1 ] [ S ]
  • 所以现在的问题转变为怎样求 d p [ 1 ] [ s t a t e ]
  • c n t [ s t a t e ] 表示当前选的节点状态是state时的边数,
  • g [ s t a t e ] 表示当前选的节点状态是state时,选的节点不连通的方案数。
  • 所以 d p [ 1 ] [ S ] = 2 c n t [ S ] g [ S ]
  • 那么只要将 g [ S ] 求出来就行了。
  • 显然,选的节点不连通一定是由多于1个联通块组成的,那么其中1个联通块可以表示为 d p [ 1 ] [ S ] ,剩下点之间的边数为 c n t [ S S ] ,如果要不连通,那么只要不连 S S S 之间的边即可,所以 c n t [ S S ] 这些边连不连都不影响结果,即: g [ S ] = S S d p [ 1 ] [ S ] 2 c n t [ S S ]
  • 这道题至此解决,时间复杂度为 O ( 3 n k )
  • 注意枚举子集时,为了避免重复,要限制必须取最低位的元素 x ,枚举时可以先把 x 抠出来再安回去。
#include<bits/stdc++.h>
#define maxs (1<<14)+10
#define inf (1<<30)
#define mod 1000000009
using namespace std;
typedef long long LL;
int read()
{
    char c=getchar();int f=1,sum=0;
    while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
    return sum*f;
}
LL bit[257];
LL dp[15][maxs],cnt[maxs],g[maxs];
int T,n,m,k,a[25][25];
void init()
{
    bit[0]=1;
    for(int i=1;i<=256;i++) bit[i]=(bit[i-1]<<1)%mod;
}
int lowbit(int x){return x&-x;}
void solve(int cas)
{
    memset(dp,0,sizeof(dp));
    memset(a,0,sizeof(a));
    n=read();m=read();k=read();
    for(int i=1,u,v;i<=m;i++)
    {
        u=read();v=read();a[u][v]++;
        if(u!=v) a[v][u]++; 
    }
    for(int sta=1;sta<(1<<n);sta++)
    {
        int pos=0;
        while(!(sta&(1<<pos))) pos++;
        cnt[sta]=cnt[sta^(1<<pos)];
        for(int j=pos;j<n;j++) if(sta&(1<<j))
        cnt[sta]+=a[pos+1][j+1];
    }
    for(int sta=1;sta<(1<<n);sta++)
    {
        int t=lowbit(sta);g[sta]=0;
        for(int s0=((sta^t)-1)&(sta^t);;s0=(s0-1)&(sta^t))
        {
            g[sta]+=dp[1][s0^t]*bit[cnt[sta^s0^t]];
            if(!s0) break;
        }
        dp[1][sta]=(bit[cnt[sta]]-g[sta])%mod;
        if(dp[1][sta]<0) dp[1][sta]+=mod;
    }
    for(int i=2;i<=k;i++)
    for(int sta=1;sta<(1<<n);sta++)
    {
        int t=lowbit(sta);
        for(int s0=((sta^t)-1)&(sta^t);;s0=(s0-1)&(sta^t))
        {
            dp[i][sta]+=dp[i-1][sta^s0^t]*dp[1][s0^t];
            if(dp[i][sta]>=mod) dp[i][sta]%=mod;
            if(!s0)break;
        }
    }
    printf("Case #%d:\n%lld\n",cas,dp[k][(1<<n)-1]);
}
int main()
{
    init();
    T=read();
    for(int cas=1;cas<=T;cas++)
    solve(cas);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39791208/article/details/81811303