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);
}
}