(截至2017-2-16前在bzoj上做的一眼水题)
bzoj上的题目链接形式:
http://www.lydsy.com/JudgeOnline/problem.php?id=题号
例如: 题号1000链接:
http://www.lydsy.com/JudgeOnline/problem.php?id=1000
(之后每道题就不粘链接了)
1000.A+B Problem
题解:a+b
代码:
#include
using namespace std;
int main()
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",a+b);
return 0;
}
1008.越狱
题解:用总方案数减去不会越狱的方案数.
总方案数:m^n
不合法方案数:第一个房间m种选择,后面n-1个犯人每人有m-1种选择,即 m*(m-1)^(n-1)
快速幂计算,两者相减即为答案。
代码:
#include
const int MOD=100003;
long long qpow(long long x,long long n)
{
long long ret=1;
while(n)
{
if(n&1)
ret=ret*x%MOD;
x=x*x%MOD;
n>>=1;
}
return ret;
}
using namespace std;
int main()
{
long long m,n;
scanf("%lld%lld",&m,&n);
long long ans=(qpow(m,n)-qpow(m-1,n-1)%MOD*m%MOD+MOD)%MOD;
printf("%lld\n",ans);
return 0;
}
1012.最大数maxnumber
题解:线段树。我不会告诉你这道题可以队列暴力
代码:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int seq[200005];
char c[5];
int main()
{
int m,d,t=0,cnt=0,l,n;
scanf("%d%d",&m,&d);
while(m--)
{
scanf("%s",c);
if(c[0]=='Q')
{
scanf("%d",&l);
t=seq[cnt+1-l];
printf("%d\n",t);
}
if(c[0]=='A')
{
scanf("%d",&n);
cnt++;seq[cnt]=(n+t)%d;
for(int i=cnt-1;i>0;i--)
{
if(seq[cnt]q[i])break;
seq[i]=seq[cnt];
}
}
}
return 0;
}
1022.小约翰的游戏John
题解:Nim游戏。。。SG函数策略证明什么的网上有,算个异或和(全为1特判)
代码:
#include
using namespace std;
int main()
{
int t,n,a,sg;
bool pd;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
sg=0;pd=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a);
sg^=a;
if(a!=1)pd=1;
}
if((sg&&pd)||(!sg&&!pd))
puts("John");
else puts("Brother");
}
return 0;
}
1034.泡泡堂BNB
题解:贪心,得分最多是排序后从蔃到蒻排名相同的比赛,如果比不过就用最蒻的来比赛,得分最少的就是对方得分最多的情况用总分减去即可。
代码:
#include
using namespace std;
int work(int a[],int b[],int n)
{
int la=1,lb=1,ra=n,rb=n,ans=0;
while(la<=ra&&lb<=rb)
{
if(a[la]>b[lb])++la,++lb,ans+=2;
else if(a[ra]>b[rb])--ra,--rb,ans+=2;
else{
if(a[la]==b[rb])ans++;
++la,--rb;
}
}
return ans;
}
int a[100005],b[100005];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
for(int i=1;i<=n;++i)scanf("%d",&b[i]);
sort(a+1,a+1+n);sort(b+1,b+1+n);
printf("%d %d\n",work(a,b,n),2*n-work(b,a,n));
return 0;
}
1047.理想的正方形
题解:滑动窗口+单调队列。
然而苯蒟蒻:暴力
代码:
#include
#define INF 2e9
using namespace std;
int mat[1005][1005],minn[1005][1005],maxn[1005][1005];
int main()
{
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
int a,b,n,ans=INF,nowmax,nowmin;
scanf("%d%d%d",&a,&b,&n);
for(int i=1;i<=a;++i)
for(int j=1;j<=b;++j)
scanf("%d",&mat[i][j]);
memset(minn,0x7f,sizeof(minn));
for(int i=1;i<=a;++i)
for(int j=1;j<=b-n+1;++j)
for(int k=j;k<=j+n-1;++k)
{
maxn[i][j]=max(maxn[i][j],mat[i][k]);
minn[i][j]=min(minn[i][j],mat[i][k]);
}
for(int i=1;i<=a-n+1;++i)
for(int j=1;j<=b-n+1;++j)
{
nowmax=0,nowmin=INF;
for(int k=i;k<=i+n-1;++k)
{
nowmax=max(nowmax,maxn[k][j]);
nowmin=min(nowmin,minn[k][j]);
if(nowmax-nowmin>=ans)break;
}
if(nowmax-nowminprintf("%d\n",ans);
return 0;
}
1087.互不侵犯King
题解:基础状压dp,记一下每行的状态,状压01表示放没放转成二进制数,cnt表示一行的棋子个数,预处理出一行相邻格子不能放,即不可行的状态lgl(可行)和upn(相邻),每行状态只跟上一行有关,dp即可。
初始化:dp[1][cnt[i]][i] = 1 (0<=i<2^n, lgl[i])
方程式:dp[i][p+cnt[j]][j] = dp[i][p][l] (2<=i<=n, 0<=j,l<2^n, cnt[l]<=p<=k-cnt[j], lgl[j]&&lgl[l]&&upn[j][l])
代码:
#include
using namespace std;
int cnt[515];
bool lgl[515], upn[515][515];
long long dp[11][85][515];
int main ()
{
//freopen (".in", "r", stdin);
//freopen (".out", "w", stdout);
int n, k;
long long ans = 0;
scanf ("%d%d", &n, &k);
int nst = 1 << n;
for (int i = 0; i < nst; ++ i)
if (!(i & (i >> 1))) {
lgl[i] = 1;
int sum = 0;
for (int j = i; j; j >>= 1)
sum += j & 1;
cnt[i] = sum;
}
for (int i = 0; i < nst; ++ i)
if (lgl[i]) for (int j = 0; j < nst; ++ j)
if (lgl[j] && !(i & j) && !(i & (j >> 1)) && !((i >> 1) & j))
upn[i][j] = 1;
for (int i = 0; i < nst; ++ i)
if (lgl[i]) dp[1][cnt[i]][i] = 1;
for (int i = 2; i <= n; ++ i)
for (int j = 0; j < nst; ++ j)
if(lgl[j]) for (int l = 0; l < nst; ++ l)
if (lgl[l] && upn[j][l])for (int p = cnt[l]; p <= k - cnt[j]; ++ p)
dp[i][p + cnt[j]][j] += dp[i - 1][p][l];
for (int i = 0; i < nst; ++ i)
ans += dp[n][k][i];
printf ("%lld\n", ans);
return 0;
}
1088.扫雷Mine
题解:递推思想,a数组记录第二行格子相邻炸弹数,f数组表示要求的第一行格子相邻炸弹数,可以递推得f[i+1]=a[i]-f[i]-f[i-1]
,分类讨论a[1], f[1], f[2]的情况,统计方案数。
代码:
#include
using namespace std;
int n,ans;
int a[10001],f[10001];
bool jud()
{
for(int i=2;i<=n;i++)
{
f[i+1]=a[i]-f[i]-f[i-1];
if(f[i+1]<0)return 0;
}
if(a[n]-f[n-1]-f[n]!=0)return 0;
return 1;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
if(a[1]==0)ans+=jud();
else if(a[1]==1)
{
f[1]=1;ans+=jud();
memset(f,0,sizeof(f));
f[2]=1;ans+=jud();
}
else {f[1]=f[2]=1;ans+=jud();}
printf("%d",ans);
return 0;
}
1192.鬼谷子的钱袋
题解:表示成二进制数是最少的,就是求m的二进制位数。
代码:
#include
using namespace std;
int main()
{
int m,ans=0;
scanf("%d",&m);
while(m)
{
m>>=1;
ans++;
}
printf("%d\n",ans);
return 0;
}
1193.马步距离
题解:贪心+暴搜
距离终点比较远的时候贪心:设终点与现在位置的横坐标差为xs,纵坐标差为ys,下一步位移的横纵坐标正负即xs。ys的符号;如果|xs|>|ys|,横坐标位移绝对值为2,纵坐标绝对值为1,否则反之。
移动到以终点为重心5*5范围内时,贪心会出现错误。此时采用暴搜即可。
代码:
#include
using namespace std;
const int f[7][7] = {
{0, 3, 2, 3, 2},
{3, 2, 1, 2, 3},
{2, 1, 4, 3, 2},
{3, 2, 3, 2, 3},
{2, 3, 2, 3, 4}
};
int main ()
{
//freopen (".in", "r", stdin);
//freopen (".out", "w", stdout);
int xp, yp, xs, ys, x, y, ans = 0;
scanf ("%d%d%d%d", &xp, &yp, &xs, &ys);
x = abs (xp - xs); y = abs (yp - ys);
while (x > 4 || y > 4) {
if (x < y) x -= 1, y -= 2;
else x -= 2, y -= 1;
x = abs (x); y = abs (y);
++ ans;
}
ans += f[x][y];
printf ("%d\n", ans);
return 0;
}
1199.汤姆的游戏
题解:标算应该是对于每个点二分一下判断,或者线段树一类。
这道题可以用队列:先把圆当成它的边平行于坐标轴的外接正方形,转换成矩形。再使用队列维护一下在每个矩形x范围内的点,暴力判断y是否在范围内;如果是圆还要判断一下到圆心距离和半径的关系。
代码:
#include
using namespace std;
struct point{double x,y;int bh;}p[10005];
struct rect{point a,b;}re[250005];
struct circ{point o;double r;}ci[250005];
int ans[10005],que[10005];
char c[2];
double pf(double q){return q*q;}
bool cmp(point A,point B){return A.x.x;}
bool cmpr(rect A,rect B){
if(A.a.x.a.x)return 1;
if(A.a.x>B.a.x)return 0;
return A.b.x.b.x;
}
bool cmpc(circ A,circ B){
if(A.o.x-A.r.o.x-B.r)return 1;
if(A.o.x-A.r>B.o.x-B.r)return 0;
return A.o.x+A.r.o.x+B.r;
}
int main()
{
int n,m,nr=0,nc=0,now,head,tail;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%s",c);
if(c[0]=='r'){++nr;scanf("%lf%lf%lf%lf",&re[nr].a.x,&re[nr].a.y,&re[nr].b.x,&re[nr].b.y);}
if(c[0]=='c'){++nc;scanf("%lf%lf%lf",&ci[nc].o.x,&ci[nc].o.y,&ci[nc].r);}
}
for(int i=1;i<=m;++i)
{scanf("%lf%lf",&p[i].x,&p[i].y);p[i].bh=i;}
sort(p+1,p+m+1,cmp);
sort(re+1,re+nr+1,cmpr);
sort(ci+1,ci+nc+1,cmpc);
now=1,head=0,tail=-1;
for(int i=1;i<=nr;++i)
{
while(p[now].x<=re[i].a.x&&now<=m)++now;
while(p[que[head]].x<=re[i].a.x&&head<=tail)++head;
while(p[now].x.b.x&&now<=m){++tail;que[tail]=now;++now;}
for(int j=head;j<=tail;++j)
{
if(p[que[j]].x>re[i].a.x
&&p[que[j]].x.b.x
&&p[que[j]].y>re[i].a.y
&&p[que[j]].y.b.y)
++ans[p[que[j]].bh];
}
}
now=1,head=0,tail=-1;
for(int i=1;i<=nc;++i)
{
while(p[now].x<=ci[i].o.x-ci[i].r&&now<=m)++now;
while(p[que[head]].x<=ci[i].o.x-ci[i].r&&head<=tail)++head;
while(p[now].x.o.x+ci[i].r&&now<=m){++tail;que[tail]=now;++now;}
for(int j=head;j<=tail;++j)
{
if(pf(p[que[j]].x-ci[i].o.x)+pf(p[que[j]].y-ci[i].o.y).r))
++ans[p[que[j]].bh];
}
}
for(int i=1;i<=m;++i)
printf("%d\n",ans[i]);
return 0;
}
1432.Function
题解:这个。。。苯蒟蒻也不是太会,找规律。发现如果这些函数以一种方式排列,第k层有2k段,当然可以将第k层转化为n-k+1层,两种方法取min,注意n==1时特判。
代码:
#include
using namespace std;
int main()
{
int n,k;
scanf("%d%d",&n,&k);
if(n==1)puts("1");
else printf("%d\n",2*min(k,n-k+1));
return 0;
}
1800.fly 飞行棋
题解:n<=20???这个数据范围简直是暴殄天物!
O(n^4)做法:暴力枚举哪4个点,判断顺次两点之间的圆弧,相对的是否相等。
O(n^2)做法:预处理圆弧长度前缀和,枚举两个点看之间的圆弧是否为周长的一半,统计直径个数,每两个不同的直径对应着一个矩形。
O(n)做法:计算直径个数时无需枚举,按顺时针顺序处理即可。
代码 O(n^2):
#include
using namespace std;
int sum[25];
int main()
{
int n,d=0,a;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a);
sum[i]=sum[i-1]+a;
}
for(int i=1;ifor(int j=i+1;j<=n;j++)
if(sum[j]-sum[i]==sum[n]>>1)
d++;
printf("%d\n",d*(d-1)/2);
return 0;
}
1968.COMMON 约数研究
题解:稍微逆向思维一下,对于每一个1<=i<=n,在<=n的范围内是n/i个数的约数,直接统计对答案的贡献即可。答案为: ∑ni=1⌊ni⌋
代码:
#include
using namespace std;
int main()
{
int n,ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
ans+=n/i;
printf("%d\n",ans);
return 0;
}
2257.瓶子和燃料
题解:感性认识一下,操作能的得到的最小正体积是这些瓶子容量的gcd(好像有一个叫裴蜀定理的东西,表示并不会),把这n个数的所有约数放在一起排序,找到最大的出现次数大于k的就约数即为答案。
代码:
#include
using namespace std;
int Div[2000005];
int main()
{
int n,num=0,cnt=0,v,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&v);
for(int j=1;j*j<=v;j++)
if(v%j==0)
{
if(j*jelse Div[++num]=j;
}
}
sort(Div+1,Div+num+1);
for(int i=num;i>=1;i--)
{
if(Div[i]!=Div[i+1])cnt=1;
else cnt++;
if(cnt>=k){printf("%d\n",Div[i]);break;}
}
return 0;
}
4300.绝世好题
题解:与其说这是一道氘磷(DP)题,不如说是一道锑(Sb)题。。。
即使是这样苯蒟蒻一开始还是算错了。bi && b(i-1) != 0 不等价于整个b序列在某二进制位全为1。
就像这样:
01
11
10
cnt[j] 记录目前二进制第 j - 1位为1的最大长度;以 a 结尾的子序列最大长度是 max {cnt[j] + 1} (0 <= j <= 31 && a 的第 j 位 = 1)。
代码:
#include
int cnt[35];
inline int getin () {
int ret = 0, ng = 1; char c = getchar ();
while (c < '0' || c > '9') {if (c == '-') ng = -1; c = getchar ();}
while (c >= '0' && c <= '9') {ret = ret * 10 + c - '0'; c = getchar ();}
return ret * ng;
}
int main () {
//freopen (".in", "r", stdin);
//freopen (".out", "w", stdout);
int n, len = 0, now, a;
n = getin ();
for (int i = 1; i <= n; ++ i) {
a = getin ();
now = 0;
for (int j = 0; j <= 30; ++ j)
if (a & (1 << j) && now < cnt[j] + 1) now = cnt[j] + 1;
for (int j = 0; j <= 30; ++ j)
if (a & (1 << j)) cnt[j] = now;
}
for (int i = 0; i <= 30; ++ i)
if (cnt[i] > len) len = cnt[i];
printf ("%d\n", len);
return 0;
}