day2模拟赛题解包括(T1腾讯邀请赛试题,T2 zoj3547,T3 zoj3548)

T1
回型矩阵
【问题描述】
  若干年之后,XJH找工作面试,面试官给了他这样一个题目。
  你有一个N*N的回型矩阵,保证N是奇数。
  例如,当N=5的时候,矩阵如下:
  
  现在,输入N,M,X,Y,你需要输出对于N*N的回型矩阵,以(X,Y)作为左上角的一个M*M的子矩阵的值。
【输入格式】
   一行,N,M,X,Y,保证N是奇数。
【输出格式】
  M*M的一个矩阵,表示答案。

【样例输入】
  5 4 2 2
【样例输出】
  17 18 19 6
  24 25 20 7
  23 22 21 8
  12 11 10 9
【数据范围】
  对于30%的数据,N<=1000
对于100%的数据,N<=1000000,M<=100,1<=X,Y<=N-M+1

题解:
这道题考试时是A了,但思考与调试时间有点久,导致2,3题的时间有些不够。这题仔细想想就能发现因为m的范围很小,所以其实就是给你一个坐标,让你求当前位置的值是多少,并且外围每一圈的都可以分为四部分,这样知道在第几圈,第几个部分,就很好求,当前的值了,注意算时不要炸,先减后乘。

附AC代码

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<cstring>
#define ll long long 
using namespace std;
ll n,m,x,y;
ll vis[105][105];
ll solve(int x,int y)
{
    int id=0,mid=(n+1)/2,cha=0;
    ll ans=0;
    if(x==mid&&y==mid) { return ans=n*n;}
    else if(x+y<=n&&x<=y) id=1,cha=x-1;
    else if(x+y<=n&&y<x) id=4,cha=y-1;
    else if(x+y>n&&y>x) id=2,cha=n-y;
    else if(x+y==n+1&&x>y) id=4,cha=y-1;
    else if(x+y>n&&y<=x&&(x+y)!=n+1) id=3,cha=n-x;
    ans=(n-cha)*cha;
    ans*=4;
    ans+=(n-2*(cha+1)+1)*(id-1);
    if(id==1) ans+=y-cha;
    if(id==2) ans+=x-cha;
    if(id==3) ans+=(n-y+1)-cha;
    if(id==4) ans+=(n-x+1)-cha;
    return ans;
}
int main()
{
    freopen("matrix.in","r",stdin);
    freopen("matrix.out","w",stdout);
    cin>>n>>m>>x>>y;
    for(ll i=x;i<x+m;i++)
    for(ll j=y;j<y+m;j++)
    {
        int a=i-x;
        int b=j-y;
        vis[a][b]=solve(i,j);
    }
    for(ll i=0;i<m;i++)
    {
        for(ll j=0;j<m-1;j++)
        printf("%lld ", vis[i][j]);
        printf("%lld\n",vis[i][m-1]);
    }
}

T2(一道素因子分解+逆元+容斥原理)
题目有链接
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3547

中文题意:
发工资
【问题描述】
  又若干年之后,XJH成为了一个上市企业的老板,然而是一个抠门的老板(XJH:???)。
  XJH的公司里面有n个员工,编号是1~n。每个人的工资是自己编号的四次方,例如,编号为5的员工,工资就是54=625。
  (员工1:???)
  然而,XJH觉得工资发的太多了,需要裁员。于是,决定把所有编号和自己互质的员工都裁掉(XJH的编号显然是n,肥水不流外人田)。
  现在,问:XJH裁员之后能省多少钱?
  
【输入格式】
  第一行输入t,表示t组数据。
  接下来t行,每行一个数字n,表示询问。
【输出格式】
  t行,每行一个数字表示答案。
  由于答案太大,mod 1,000,000,007 即可。
【样例输入】
  2
  4
  5
【样例输出】
  82
  354
【样例解释】
  Case1: sum=1+3*3*3*3=82
  Case2: sum=1+2*2*2*2+3*3*3*3+4*4*4*4=354
【数据范围】
  对于30%的数据,n<=106
  对于100%的数据,n<=108,t<=100

题解:
一开始就想到要连续的求1~n的每个数的4次方之和,在减去所有与其不互质的数的4次方,就是答案。于是这里推导出四次方求和公式,Σi=1~n,i的4次方,=n*(n+1)(2n+1)(3n方+3n-1)/30,因为这里有除法,所以要用到逆元,因为mod是质数,所以直接是30的mod-2次方,另写代码求得应为233333335,
算到这里就需要质因数分解了,求出n以下所有质因子,并且所有质因子的倍数只要小于n,都需要减去,再运用容斥原理,加上有奇数个质因子的(奇数个质因子乘积的倍数的四次方(小于n))减去有偶数个质因子的。
可能是数据水,20ms过。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<vector> 
#define ll long long
using namespace std;
const int maxn=1005;
const int mod=1e9+7;
int t;
ll n,cnt,now,fac[1005],ans,res;
ll sum(ll n){
    ll  ans=n%mod;
        ans=(ans*(n+1)%mod)%mod;
        ans=(ans*(2*n+1)%mod)%mod;
        ans=(ans*((3*n*n)%mod+(3*n)%mod-1)%mod)%mod;
        ans=(ans*233333335)%mod;
    return ans; 
}
ll n4(ll x)
{
    return x%mod*x%mod*x%mod*x%mod;
}
void dfs(ll tot,ll now,bool flag,ll n,ll &ans)//容斥原理 
{
    if(tot>=cnt) return ;
    ll tmp=now*fac[tot];
    dfs(tot+1,now,flag,n,ans);
    if(flag) ans=(ans+sum(n/tmp)*n4(tmp)%mod)%mod;
    else ans=(ans-sum(n/tmp)*n4(tmp)%mod)%mod;
    dfs(tot+1,tmp,!flag,n,ans);
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld",&n);
        now=n,cnt=0;
        for(int i=2;i*i<=now;i++)
        {
            if(now%i==0)
            {
                fac[cnt++]=i;
                while(now%i==0) now/=i;
            }
        }
        if(now!=1) fac[cnt++]=now;
        ans=0;
        dfs(0,1,1,n,ans);
        res=sum(n);
        res=((res-ans)%mod+mod)%mod;
        printf("%lld\n",res);
    } 
}

T3(二分图匹配+代码能力。。。)
题目来源zoj 2548
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3548
放按钮
【问题描述】
   又过去了若干年,XJH看淡一切,回归本源,决定还是继续写代码。
   然而这时候的XJH已经把写代码的功夫忘差不多了,于是只能干最简单的活:给界面上放按钮。
   但这不代表着XJH是一个没有追求的程序员,由于XJH的强迫症,他放的按钮,必须是工整,对称的。
   假设XJH面对着一个R*C的界面,界面的每一个格子初始时候只有黑白两种颜色。
   现在,XJH要在上面放r*c个按钮,每个按钮都是一个正方形,边长任意。
   但是,由于XJH的强迫症,所有按钮和界面的边界,按钮两两之间,间隔必须是一样的!并且,这个间隔还要小于等于按钮的边长!
   例如,如下图就是一个合法的放置。
   
   7*7的格子上面,放了2*2个按钮。间隔1<=按钮边长2。
   当按钮放好之后,XJH就要给按钮上色了,按钮要染成白色,其他位置要染成黑色。
   每次染色,XJH可以选择任意一个矩形区域,一起染成白or黑。但是,某一个格子不能先被染了某种色,又被染成另一种颜色(也就是异色的染色矩形,不能相交)。
   现在,已知每次染色消耗t的代价,放按钮没有代价,问:最少需要多少的代价,可以让XJH完成放按钮+染色的任务。
【输入格式】
  第一行R,C,r,c,t
  接下来R行,每行C个字符,0代表黑色,1代表白色
【输出格式】
  一个数字,表示最小代价。
【样例输入】
  3 5 1 2 1
  00000
  01110
  01110
【样例输出】
  2
【样例解释】
  两个按钮只能放在
  00000
  0X0X0
  00000
  这两个位置,那么最终的样子是:
  00000
  01010
  00000
  染色的时候显然两次就可以染成目标样式。
【数据范围】
  100%的数据保证3<=R,C<=2000,1<=r,c<=200,t<=1000,保证有解。

题解:
设每个方块的边长为k2,
首先分两种情况 ,n==m或者n!=m,若不等,则说明(N-k2*n)/(n+1)==(M-k2*m)/(m+1),此种情况只有一组解;
如果n==m,则N必须==M,否则无解。
显然如果某个方阵内有黑色点,必然需要对该方阵染一次色,这个直接枚举判断。
对于那些间隙,显然对于某行含有白色点的间隙,直接一段染始终是不可能比染一行更加优秀的,列也是一样。
所以我们要做的,就是计算最少要染多少次行和列。
对于某些点,可以横行覆盖也可以竖行覆盖,看的出就是二分匹配求最小点覆盖(这里的“行”和“列”指的是一整条间隙,是一个矩阵)
如果某些点,不可能被列染色(竖着整列一起染色),即它上下是方阵,而不是行间隙和列间隙的相交位置,则它只能被行染色。这样,该行必须被染色。反之同理。
剩下有些行和列,只在交汇处有白色点,要求这些行和列的最小染色次数。
将有公共白色点的行和列连边,很容易转化为二分图最大点覆盖==二分图最大匹配。

#include<iostream>
#include<cstring>
#include<stdio.h>
#include<vector>
using namespace std;
const int inf=0x3f3f3f3f;
int res,N,M,t,sum[2020][2020],match[210],n,m,cntn[210],cntm[210],k1,k2,k,ans,tot;
char s[2020][2020];
bool vis[210],mp[210][210];
vector<int> v;
bool find(int x)
{
    for(int i=0;i<=m;i++)
    if(!vis[i]&&mp[x][i])
    {
        vis[i]=1;
        if(match[i]==-1||find(match[i]))
        {
            match[i]=x;
            return 1;
        }
    }
    return 0;
}
bool ok(int N,int k2,int n)//k2shibianchang
{
    if((N-k2*n)<=0||(N-k2*n)%(n+1)) return 0;
    if((N-k2*n)>k2*(n+1))           return 0;
    return 1;
}
int main()
{
    while(~scanf("%d%d%d%d%d",&N,&M,&n,&m,&t))
    {
        int i,j;
        for(int i=0;i<N;i++) scanf("%s",s[i]);
        v.clear();   res=inf;
        if(n==m)
        {
            if(N==M) 
            {for(int i=1;i*n<N;i++) if(ok(N,i,n)) v.push_back(i);}
            else 
            {
                printf("-1\n");
                continue;
            }
        }
        else 
        {
            k2=(((m+1)*N-(n+1)*M)/(n-m));
            k1=(N-k2*n)/(n+1);
            if(k2<=0||k1<=0||k1>k2||k2*n+k1*(n+1)-N||k2*m+k1*(m+1)-M)
            {
                printf("-1\n");
                continue;
            }
            v.push_back(k2);
        }
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=N;i++)
        for(int j=1,k=0;j<=M;j++)
        {
            k+=s[i-1][j-1]-'0';
            sum[i][j]=sum[i-1][j]+k;
        }
        for(int p=0;p<v.size();p++)
        {
            memset(cntn,0,sizeof(cntn));
            memset(cntm,0,sizeof(cntm));
            memset(match,-1,sizeof(match));
            memset(mp,0,sizeof(mp));
            k2=v[p],k1=(N-k2*n)/(n+1),ans=0,k=k1+k2;
            for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            if(sum[i*k][j*k]-sum[i*k-k2][j*k]-sum[i*k][j*k-k2]+sum[i*k-k2][j*k-k2]!=k2*k2) ans++;
            for(int i=0;i<=n;i++)
            for(int j=0;j<=m;j++)
            {
                cntn[i]+=sum[i*k+k1][j*k+k1]-sum[i*k][j*k+k1]-sum[i*k+k1][j*k]+sum[i*k][j*k];
                cntm[j]+=sum[i*k+k1][j*k+k1]-sum[i*k][j*k+k1]-sum[i*k+k1][j*k]+sum[i*k][j*k]; 
            };
            for(int i=0;i<=n;i++)
            for(int j=0;j<=m;j++)
            {
                if(sum[i*k+k1][j*k+k1]-sum[i*k][j*k+k1]-sum[i*k+k1][j*k]+sum[i*k][j*k]==0) continue;
                if(sum[i*k+k1][M]-sum[i*k][M]-cntn[i]||sum[N][j*k+k1]-sum[N][j*k]-cntm[j]) continue;
                mp[i][j]=1;
            };
            for(int i=0;i<=n;i++) if(cntn[i]-sum[i*k+k1][M]+sum[i*k][M])  ans++;
            for(int j=0;j<=m;j++) if(cntm[j]-sum[N][j*k+k1]+sum[N][j*k])  ans++;
            for(int i=0;i<=n;i++)
            {
                memset(vis,0,sizeof(vis));
                if(find(i)) ans++;
            }
            if(res>ans) res=ans;    
        }
        printf("%d\n",res*t);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_42618295/article/details/81782985