2023牛客暑期多校训练营4

 Bobo String Construction 结论,字符串哈希

Election of the King 二分查找

Merge the squares! 递归模拟,辗转相除法

Qu'est-ce Que C'est? DP,前缀和优化

We are the Lights 思维,倒推

猜测是,把n个字符全填0或者1是最优的。所以只需要字符串哈希检查是否两个串里面含有给定串即可。 

 

#include <bits/stdc++.h>

using namespace std;
typedef long long int ll;
# define p1 13331
# define p2 1331
# define mod1 998244353
# define mod2 1000000007
ll base1[5000+10],base2[5000+10],sum1[5000+10],sum2[5000+10],ans1,ans2;
int n,m;
ll getsum1(int l,int r)
{
    ll ans=((sum1[r]-(sum1[l-1]*base1[r-l+1]%mod1)%mod1)%mod1+mod1)%mod1;
    return ans;
}
ll getsum2(int l,int r)
{
    ll ans=((sum2[r]-(sum2[l-1]*base2[r-l+1]%mod2)%mod2)%mod2+mod2)%mod2;
    return ans;
}
int main()
{
    int t;
    cin>>t;
    base1[0]=base2[0]=1;
    for(int i=1; i<=5000; i++)
    {
        base1[i]=p1*base1[i-1]%mod1;
        base2[i]=p2*base2[i-1]%mod2;
    }

    while(t--)
    {
        cin>>n;
        string s;
        cin>>s;
        string pres=s;
        m=s.length();
        s=" "+s;
        ans1=0,ans2=0;
        for(int i=1; i<=m; i++)
        {
            ans1=(ans1*p1%mod1+(int)(s[i]-'0'))%mod1;
            ans2=(ans2*p2%mod2+(int)(s[i]-'0'))%mod2;
        }
        string temp1=s;
        for(int i=1; i<=n; i++)
        {
            temp1+='1';
        }
        temp1+=pres;
        string temp2=s;
        for(int i=1; i<=n; i++)
        {
            temp2+='0';
        }
        temp2+=pres;

        int len1=pres.length()*2+n,len2=len1;

        for(int i=1; i<=len1; i++)
        {
            sum1[i]=(sum1[i-1]*p1%mod1+(int)(temp1[i]-'0'))%mod1;
            sum2[i]=(sum2[i-1]*p2%mod2+(int)(temp1[i]-'0'))%mod2;
        }
        int flag=0;
        for(int i=2; i+m-1<len1; i++)
        {
            ll now1=getsum1(i,i+m-1);
            ll now2=getsum2(i,i+m-1);
            if(now1==ans1&&now2==ans2)
            {
                flag=1;
                break;
            }
        }
        if(flag==0)
        {
            for(int i=1; i<=n; i++)
            {
                cout<<1;
            }
            cout<<'\n';
            continue;
        }
        temp1=temp2;
        for(int i=1; i<=len1; i++)
        {
            sum1[i]=(sum1[i-1]*p1%mod1+(int)(temp1[i]-'0'))%mod1;
            sum2[i]=(sum2[i-1]*p2%mod2+(int)(temp1[i]-'0'))%mod2;
        }
        flag=0;
        for(int i=2; i+m-1<len1; i++)
        {
            ll now1=getsum1(i,i+m-1);
            ll now2=getsum2(i,i+m-1);
            if(now1==ans1&&now2==ans2)
            {
                flag=1;
                break;
            }
        }
        if(flag==0)
        {
            for(int i=1; i<=n; i++)
            {
                cout<<0;
            }
            cout<<'\n';
            continue;
        }
        cout<<-1<<'\n';

    }
    return 0;
}

 

首先,对数组进行排序。以1 2 3 4 5为例,>=3的全部都投向5,否则都投给1。我们只需要获得当前[L,R]区间的(A[L]+A[R]) /2,如果(A[L]+A[R])恰好整除2,则全部大于等于这个值的都投给右侧,如果不能整除2,即为某.5,同样全部大于这个值的都投给右侧,故采用upper_bound,再pos--,即获得投给左侧的全部。

# include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
struct node
{
    int id,val;
    friend bool operator<(node x, node y)
    {
        return x.val<y.val;
    }
};
struct node s[1000000+10];

bool cmp(struct node x, struct node y)
{
    return x.val<y.val;
}
int main ()
{

    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&s[i].val);
        s[i].id=i;
    }
    sort(s+1,s+1+n,cmp);
    int l=1,r=n;
    for(int i=1;i<=n-1;i++)
    {
        int now=(s[l].val+s[r].val)/2;
        struct node temp;
        temp.id=0;
        temp.val=now;
        int mid=upper_bound(s+l,s+1+r,temp)-s;
        mid--;
        int flag=0;
        if(s[mid].val==now)
            flag++;

        if(mid-l+1>=r-mid)
        {
            r--;
        }
        else
        {
            l++;
        }
    }
    cout<<s[l].id;
    return 0;
}

 

 首先,当n包含某个因子的时候,如果这一因子小于等于7,大于1,就可以合并每个因子*因子的正方形。但当n为质数的时候,这一方法失效。故考虑构造正方形n*n为四部分,

n*n=(a+b)*(a+b)。对于黄色长方形,按照辗转相处法构造,即每次以较短边为边长构造正方形。对于蓝色正方形,我们继续按照这一规则,递归构造。而当前n选择的a,b值,可以暴力枚举n的a值,检验这个a*b的长方形构造出的正方形个数是否满足2*cnt+2<=50.即我们假定蓝色已经递归构造成功。值得注意的是,因为任意正方形一定是n*n的平方数,故我们一定可以找到a,b。而对于是否有界,并不会做充分证明。

 

 

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
int ans[1010],n;
bool check(int x,int y)
{
    if(y==0)
    {
        return x<=7;
    }
    int cnt=0;
    int a=x,b=y,c;
    while(b)
    {
        cnt+=a/b;
        c=a;
        a=b;
        b=c%b;
    }
    return cnt*2+2<=50;
}
struct node
{
    int x,y,len;
};
stack<node>temp;
void dfs(int,int,int);
void workC(int,int,int,int);
void workR(int x,int y,int row,int col) //行短
{
     if(row==1)
        return ;
    int cnt=col/row;
    for(int i=1;i<=cnt;i++)
    {
        dfs(x,y+(i-1)*row,row);
    }
    int yu=col%row;
    if(yu)
    {
        workC(x,y+(cnt)*row,row,yu);
    }
}
void workC(int x,int y,int row,int col)
{
    if(col==1)
        return ;

    int cnt=row/col;
    for(int i=1;i<=cnt;i++)
    {
        dfs(x+(i-1)*col,y,col);
    }
    int yu=row%col;
    if(yu)
    {
        workR(x+cnt*col,y,yu,col);
    }
}
void dfs(int x,int y,int len)
{
    if(len==1)
        return;
    struct node now;
    now.len=len;
    now.x=x;
    now.y=y;
    temp.push(now);
    if(ans[len]==0)
        return ;
    int a=len-ans[len],b=ans[len];
    workR(x+a,y,b,a);
    workC(x,y+a,a,b);
    dfs(x,y,a);
    dfs(x+a,y+a,b);
    return ;
}
int main()
{
   cin>>n;
   for(int i=1;i<=n;i++)
   {
       ans[i]=-1;
       for(int j=0;j<=i/2;j++)
       {
           if(check(i-j,j))
           {
               ans[i]=j;
              // break;
           }
       }
   }

   dfs(1,1,n);
   cout<<temp.size()<<'\n';

   while(!temp.empty())
   {
       struct node now=temp.top();
       temp.pop();
       cout<<now.x<<" "<<now.y<<" "<<now.len<<'\n';
   }

    return 0;
}

 

首先如果仅看两两之和大于等于0是很难转移的。不妨强制两种块,一种是一个正数,一种是一个负数和一个正数。这样可以获得类似于 负正正正负正正 的局面。而至于为什么不是正负组合,主要是因为,负数在前面放置,可以满足前面块和负数之和不为负数,也方便存储。缺点是,不能包含最后一个是负数的情况,故需要特判,由dp[n-1][i]*i转移。

写出暴力n^3的DP后,可以发现是可以进行前缀和优化的,且需要两个前缀和。第二个前缀和的推导,可以借助暴力程序的画图,找出枚举规律。可见,特殊的dp枚举优化,可以通过画图直观解决。

# include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
# define mod 998244353
ll dp[5010][5010];
int sum[5010][5010];
ll sumji[5010][5010];
int main()
{
    int n;
    ll m;
    cin>>n>>m;
    if(n==1)
    {
        cout<<m+m+1;
        return 0;
    }
    dp[0][0]=1;
    for(int i=0; i<=m; i++)
    {
        sum[0][i]=1;
    }
    for(int i=0; i<=m; i++)
    {
        dp[1][i]=1;
        sum[1][i]=i+1;
        if(i)
        {
            sumji[1][i]=(dp[1][i]*(ll)i)%mod;
            sumji[1][i]=(sumji[1][i-1]+sumji[1][i])%mod;
        }
    }
    for(int i=0; i<=m; i++)
    {
        for(int j=0; j<=m; j++)
        {
            dp[2][i]=(dp[2][i]+dp[1][j])%mod;
        }
        for(int k=-m; k<=-1; k++)
        {
            int zheng=i-k;
            if(zheng<=m&&zheng>=0)
            {
                dp[2][i]=(dp[2][i]+1)%mod;
            }
        }
        sumji[2][i]=(dp[2][i]*(ll)i)%mod;
        sum[2][i]=dp[2][i];
        if(i)
        {
            sum[2][i]+=sum[2][i-1];
            sum[2][i]%=mod;
            sumji[2][i]=(sumji[2][i]+sumji[2][i-1])%mod;
        }
    }
    for(int i=3; i<=n; i++)
    {
        for(int j=0; j<=m; j++)
        {
            dp[i][j]=(dp[i][j]+sum[i-1][m])%mod;

            ll temp=((sum[i-2][m]-sum[i-2][m-j])%mod+mod)%mod;
            
            dp[i][j]=(((dp[i][j]+sumji[i-2][m-j])%mod+(ll)(m-j)*temp%mod)%mod)%mod;
            sumji[i][j]=dp[i][j]*(ll)(j)%mod;
            sum[i][j]=dp[i][j];
            if(j)
            {
                sumji[i][j]=(sumji[i][j]+sumji[i][j-1])%mod;
                sum[i][j]=(sum[i][j]+sum[i][j-1])%mod;
            }
        }
    }
    ll ans=0;
    ans+=sum[n][m];
    ans%=mod;
    ans+=sumji[n-1][m];
    ans%=mod;
    cout<<ans;
    return 0;
}

 

 倒着推,但凡倒着推先点亮的,必定点亮,反之已然。然后就可以画图解决去重问题,利用点亮行的数量,关闭行的数量,点亮和关闭列的数量即可。

# include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
int bookhang[1000000+10],booklie[1000000+10],n,m;
struct node
{
    int flag,id,on;
};
struct node s[1000000+10];
int main()
{
    cin.tie(0);
    ios::sync_with_stdio(0);
    int n,m;
    cin>>n>>m;
    int t;
    cin>>t;
    for(int i=1;i<=n;i++)
    {
        bookhang[i]=-1;
    }
    for(int i=1;i<=m;i++)
    {
        booklie[i]=-1;
    }
    for(int i=1;i<=t;i++)
    {
        string ch;
        cin>>ch;
        int id;
        string on;
        cin>>id>>on;
        if(ch[0]=='r')
        {
            s[i].flag=1;
        }
        else
        {
            s[i].flag=0;
        }
        s[i].id=id;
        if(on[1]=='n')
        {
            s[i].on=1;
        }
        else
        {
            s[i].on=0;
        }
    }

    ll ans=0;
    ll lianghang=0,guanhang=0,lianglie=0,guanlie=0;
    for(int i=t;i>=1;i--)
    {
        if(s[i].flag==1)
        {
            if(s[i].on==1)
            {
                if(bookhang[s[i].id]==-1)
                {
                    ans+=(m-lianglie-guanlie);
                    bookhang[s[i].id]=1;
                    lianghang++;
                }
            }
            else
            {
                if(bookhang[s[i].id]==-1)
                {
                    bookhang[s[i].id]=1;
                    guanhang++;
                }
            }
        }
        else
        {
            if(s[i].on==1)
            {
                if(booklie[s[i].id]==-1)
                {
                    ans+=(n-lianghang-guanhang);
                    booklie[s[i].id]=1;
                    lianglie++;
                }
            }
            else
            {
                if(booklie[s[i].id]==-1)
                {
                    booklie[s[i].id]=1;
                    guanlie++;
                }
            }
        }
    }

    cout<<ans;

   return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qinsanma and Code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值