2020级训练赛10.25

矩阵填数

一、题目

点此看题

二、解法

观察数组范围,发现 h , w h,w h,w特别大,而限制只有至多 10 10 10条,我们考虑离散化
怎么个离散化法呢?考虑将每一个限制的四个边延长,这样就将大矩形切割成了至多 20 × 20 20\times 20 20×20个小矩形,且这些小矩形的性质是一样的(被哪些限制包含)。
具体的离散化操作我们把它当做切木棍,对于 [ l , r ] [l,r] [l,r],我们这样离散化:
在这里插入图片描述
也就是我们把 l , r l,r lr各切一刀,在 l l l加上一个左括号, l − 1 l-1 l1加上一个右括号, r r r加上一个左括号, r + 1 r+1 r+1加上一个右括号,离散化就变成了括号匹配问题,排序后匹配相邻的括号即可。

离散化后,我们发现每个限制中只要中只要有一个小矩形满足限制就可以了,由于限制数很小,考虑状态压缩,设 d p [ i ] [ s ] dp[i][s] dp[i][s]为前 i i i个小矩形满足状压后为 s s s的条件的方案数,我们先考虑每一个小矩形:
在这里插入图片描述
中间的那个小矩形被两个限制所包含,但是我们只能满足要求最大值为 3 3 3的限制,所以我们记小矩形的状态为 10 10 10(二进制), d p dp dp时拿原状态或上小矩形的状态就可以得到目标状态。我们再记 g [ i ] = ( M i n − 1 ) s g[i]=(Min-1)^s g[i]=(Min1)s s s s为小矩形的面积),即无法满足任何一个限制的情况, f [ i ] = M i n s − g [ i ] f[i]=Min^{s}-g[i] f[i]=Minsg[i]为能满足最大值最小的限制的情况(我们也只能满足这些限制),那样我们就能写出如下 d p dp dp方程(记小矩形状态为 S S S):
{ d p [ i ] [ j ∣ S ] = d p [ i − 1 ] [ j ] × f [ i ] d p [ i ] [ j ] = d p [ i − 1 ] [ j ] × g [ i ] \begin{cases} dp[i][j|S]=dp[i-1][j]\times f[i] \\ dp[i][j]=dp[i-1][j]\times g[i] \end{cases} {dp[i][jS]=dp[i1][j]×f[i]dp[i][j]=dp[i1][j]×g[i]
最后输出 d p [ m ] [ 2 n − 1 ] dp[m][2^n-1] dp[m][2n1]即可( m m m是小矩形的个数),时间复杂度 O ( 4 n 2 × 2 n ) O(4n^{2}\times 2^n) O(4n2×2n)

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
const int MOD = 1e9+7;

const int MAXN = 10005;
int read()
{
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int T,h,w,m,n,cnt,oth,dp[505][1<<10];
int x1[MAXN],y1[MAXN],x2[MAXN],y2[MAXN],v[MAXN];
bool vx[MAXN][2],vy[MAXN][2];
vector<pair<int,int> > x,y;
struct node
{
    int f,g,s;
}a[505];
void add(int z,bool d,bool type)
{
    if(!type) x.push_back(make_pair(z,d)),vx[z][d]=1;
    else y.push_back(make_pair(z,d)),vy[z][d]=1;
}
int qkpow(int a,int b)
{
    int res=1;
    while(b>0)
    {
        if(b&1) res=res*a%MOD;
        a=a*a%MOD;
        b>>=1;
    }
    return res;
}
signed main()
{
    T=read();
    while(T--)
    {
        h=read();w=read();m=read();n=read();
        cnt=oth=0;
        memset(vx,0,sizeof vx);
        memset(vy,0,sizeof vy);
        memset(dp,0,sizeof dp);
        x.clear();y.clear();
        add(1,0,0);add(h,1,0);
        add(1,0,1);add(w,1,1);
        for(int i=1;i<=n;i++)
        {
            x1[i]=read();y1[i]=read();x2[i]=read();y2[i]=read();v[i]=read();
            if(x1[i]>1 && !vx[x1[i]-1][1])
                add(x1[i]-1,1,0);
            if(!vx[x1[i]][0])
                add(x1[i],0,0);
            if(x2[i]<h && !vx[x2[i]+1][0])
                add(x2[i]+1,0,0);
            if(!vx[x2[i]][1])
                add(x2[i],1,0);
            if(y1[i]>1 && !vy[y1[i]-1][1])
                add(y1[i]-1,1,1);
            if(!vy[y1[i]][0])
                add(y1[i],0,1);
            if(y2[i]<w && !vy[y2[i]+1][0])
                add(y2[i]+1,0,1);
            if(!vy[y2[i]][1])
                add(y2[i],1,1);
        }
        sort(x.begin(),x.end());
        sort(y.begin(),y.end());
        for(int i=0;i<x.size();i+=2)
            for(int j=0;j<y.size();j+=2)
            {
                int a1=x[i].first,a2=x[i+1].first;
                int b1=y[j].first,b2=y[j+1].first;
                int siz=(a2-a1+1)*(b2-b1+1),s=0,Min=m;
                for(int k=1;k<=n;k++)
                {
                    if(a1>=x1[k] && a2<=x2[k] && b1>=y1[k] && b2<=y2[k])
                    {
                        if(Min>v[k])
                        {
                            Min=v[k];
                            s=0;
                        }
                        if(Min==v[k]) s|=(1<<k-1);
                    }
                }
                int g=qkpow(Min-1,siz),f=qkpow(Min,siz)-g;
                if(!s) oth+=siz;
                else  a[++cnt]=node{f,g,s};
            }
        dp[0][0]=1;
        for(int i=1;i<=cnt;i++)
        {
            for(int j=0;j<(1<<n);j++)
                dp[i][j|a[i].s]=(dp[i][j|a[i].s]+dp[i-1][j]*a[i].f)%MOD;
            for(int j=0;j<(1<<n);j++)
                dp[i][j]=(dp[i][j]+dp[i-1][j]*a[i].g)%MOD;
        }
        printf("%d\n",(dp[cnt][(1<<n)-1]*qkpow(m,oth)%MOD+MOD)%MOD);
    }
}

魔方俱乐部

一、题目

点此看题

二、解法

缩点之后无脑 d f s dfs dfs,不展开。

#include <cstdio>
#include <vector>
#include <cstring>
#include <stack>
using namespace std;
const int MAXN = 200005;
int read()
{
    int x=0,flag=1;
    char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return x*flag;
}
int n,tot,sum,a[MAXN],f[MAXN],val[MAXN],in[MAXN];
int Index,cnt,dfn[MAXN],low[MAXN],mark[MAXN],ans[MAXN];
bool vis[MAXN];
stack<int> s;
vector<int> G[MAXN];
struct edge
{
    int v,next;
} e[MAXN];
void dfs(int u)
{
    dfn[u]=low[u]=++Index;
    in[u]=1;
    s.push(u);
    for(int i=f[u]; i; i=e[i].next)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            dfs(v);
            low[u]=min(low[u],low[v]);
        }
        else if(in[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        int v;
        cnt++;
        do
        {
            v=s.top();
            in[v]=0;
            mark[v]=cnt;
            s.pop();
        }
        while(u!=v);
    }
}
void count(int u)
{
    vis[u]=1;
    ans[u]+=val[u];
    for(int i=0; i<G[u].size(); i++)
    {
        int v=G[u][i];
        if(!vis[v])
            count(v);
        ans[u]+=ans[v];
    }
}
int main()
{
    n=read();
    for(int i=1; i<=n; i++)
        a[i]=read();
    for(int i=1; i<=n; i++)
    {
        int j=read();
        e[++tot]=edge{j,f[i]},f[i]=tot;
    }
    for(int i=1; i<=n; i++)
        if(!dfn[i])
            dfs(i);
    for(int i=1; i<=n; i++)
    {
        val[mark[i]]+=a[i];
        for(int j=f[i]; j; j=e[j].next)
        {
            int v=e[i].v;
            if(mark[i]!=mark[v])
            {
                in[mark[v]]++;
                G[mark[i]].push_back(mark[v]);
            }
        }
    }
    for(int i=1; i<=cnt; i++)
    {
        if(!in[i])
            count(i);
    }
    for(int i=1; i<=n; i++)
        printf("%d\n",ans[mark[i]]);
}

手机号码

一、题目

点此看题

二、解法

以前基本上没有写过数位 d p dp dp的题,今天来先学一下吧。
数位 d p dp dp的基本思路就是先把 d f s dfs dfs版的爆搜打出来,然后套个记忆化就 A A A了。
说着感觉特别简单,实际上也很简单
对于本题,定义 d p [ i ] [ s 0 ] [ s 1 ] [ s 2 ] [ s 3 ] [ p r e ] dp[i][s_0][s_1][s_2][s_3][pre] dp[i][s0][s1][s2][s3][pre]表示当前是第 i i i位, s 0 s_0 s0:是否紧贴着数值限制, s 1 s_1 s1:是否有三连等, s 2 s_2 s2:是否有二连等, s 3 s_3 s3: 4 , 8 4,8 4,8出现没有的状态压缩, p r e pre pre:上一个数,转移并不难。
怎么算时间复杂度呢?把状态数 × \times ×转移次数(因为是记忆化搜索),就得到了时间复杂度。

#include <cstdio>
#include <cstring>
#define int long long
int read()
{
    int x=0,flag=1;
    char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=x*10+c-'0',c=getchar();
    return x*flag;
}
int L,R,num[12],init[10]= {0,0,0,0,1,0,0,0,2,0};
int dp[12][2][2][2][4][11];
int dfs(int pos,bool equal,bool tri,bool bi,int lck,int pre)
{
    if(lck==3) return 0;
    if(pos==0) return tri;
    if(dp[pos][equal][tri][bi][lck][pre]!=-1) return dp[pos][equal][tri][bi][lck][pre];
    int t=0;
    for(int i=0; i<10; i++)
    {
        if(equal && i>num[pos]) break;
        t+=dfs(pos-1,equal&(i==num[pos]),tri||(bi&(pre==i)),pre==i,lck|init[i],i);
    }
    return dp[pos][equal][tri][bi][lck][pre]=t;
}
int get(int x)
{
    memset(dp,-1,sizeof dp);
    memset(num,0,sizeof num);
    int t=0,ret=0;
    while(x>0) num[++t]=x%10,x/=10;
    for(int i=1; i<=num[11]; i++)
    {
        ret+=dfs(10,i==num[11],0,0,init[i],i);
    }
    return ret;
}
signed main()
{
    L=read();
    R=read();
    printf("%lld\n",get(R)-get(L-1));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值