速度训练2 题解

2 篇文章 0 订阅
1 篇文章 0 订阅

6题题目按顺序如下.
http://poj.org/problem?id=3685
http://poj.org/problem?id=3977
http://poj.org/problem?id=3244
http://poj.org/problem?id=2443
http://acm.hdu.edu.cn/showproblem.php?pid=6198
http://acm.hdu.edu.cn/showproblem.php?pid=6199

题解

A

求大小为n*n的奇怪矩阵中第k小的数的值.
这题坑在不知道数据有多少组,不知道怎么办.
不过如果可以 O(1) O ( 1 ) 出答案,想必 n n 的范围至少可以开到109,我们不妨就试试 O(n) O ( n ) 以上的算法.
考虑二分答案,判断矩阵中是否有 k1 k − 1 个数比它小.
我们来看公式 Aij=i2+105×i+j2105×j+i×j. A i j = i 2 + 10 5 × i + j 2 − 10 5 × j + i × j .
原式 =i2+(105+j)×i+j2105×j. = i 2 + ( 10 5 + j ) × i + j 2 − 10 5 × j .
转化为了一个以 i i 为未知数的一元二次函数.
它的对称轴在x轴的负半轴,即当 j>0 j > 0 时,函数值是和 i i 单调的.
那么对于纵列函数值总是递增的,显然可以对每一列二分答案,看看有多少个比二分出来的mid小.
这是一个二分套二分,复杂度 n×log(n)×log(max) n × l o g ( n ) × l o g ( m a x ) ,在时限内可过.

#include<cstdio> //Ithea Myse Valgulious
#include<cctype>
#include<set>
#include<bitset>
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return 0&pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const ll inf=1e16;
ll n;

ll suan(ll i,ll j){
return i*i+100000*(i-j)+j*j+i*j;
}//这里我用了浮点数1e5运算导致TLE,调了我两个小时.

ll judge(ll x){
ll cnt=0,i;
for (i=1;i<=n;++i){
  ll l=1,r=n+1;
  for (;l<r;){
    ll mid=l+r>>1;
    suan(mid,i)<x?l=mid+1:r=mid;
    }
  cnt+=l-1;
  }return cnt;
}

int main(){
for (int t=read();t--;){
  ll l=-inf,r=inf,k; 
  scanf("%lld%lld",&n,&k);
  for (;l<r;){
    ll mid=l+r>>1;
    judge(mid)<k?l=mid+1:r=mid;
    }
  printf("%lld\n",l-1);
  }
}

B

求一个非空子集使得集合内数的和的绝对值最小,满足条件使得集合中数的个数最少.
观察到数据范围是 35 35 ,不能状压枚举,再根据 352=17.5 35 2 = 17.5 在状压枚举的范围内,可以轻松知道标算是折半枚举,再思考一下发现折半枚举显然可行.
又观察到求的答案的内容是满足第一个的条件下要求第二个最小,可以想到用 pair p a i r 存储答案,直接用 min m i n 运算就可以更新答案了.
我们先枚举一半,将这一半的所有非空子集的和装到一个 map m a p 里.
然后枚举后一半的子集和 tmp t m p ,在 map m a p 里查找最接近 tmp − t m p 的值,并扫一下查出来的值的前后一个(有可能更优),并更新答案.
最后的 pair p a i r 即为答案.

#include<cstdio> //Ithea Myse Valgulious
#include<cctype>
#include<set>
#include<bitset>
#include<vector>
#include<algorithm>
#include<iostream>
#include<map>
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const int _=150;
const ll inf=1e18;
ll a[_];
typedef pair<ll,int> llx;
map<ll,int> mp;
#define abs(x) (x)>0?(x):-(x)
int main(){
for (int i,n,j;n=read();){
  for (i=0;i<n;++i) read(a[i]);
  llx ans=llx(inf,0);
  mp.clear();
  int nlim=n>>1,llim=n-nlim;
  for (i=1;i<1<<nlim;++i){
    ll tmp=0;int can=__builtin_popcount(i);//集合里有多少个数可以直接算i的二进制表示中有多少个1.
    for (j=0;j<nlim;++j) if (i>>j&1) tmp+=a[j];
    if (!mp.count(tmp)||mp[tmp]>can) mp[tmp]=can;
    llx t=llx(abs(tmp),can);
    ans=min(ans,t);
    }
  for (i=1;i<1<<llim;++i){
    ll tmp=0;int can=__builtin_popcount(i);
    for (j=0;j<llim;++j) if (i>>j&1) tmp+=a[j+nlim];
    llx t=llx(abs(tmp),can);
    ans=min(ans,t);
    map<ll,int>::iterator it=mp.lower_bound(-tmp);
    if (it!=mp.end()){
      t=llx(abs(it->first+tmp),can+it->second);
      ans=min(ans,t);
      }
    if (it!=mp.begin()){
      --it,t=llx(abs(it->first+tmp),can+it->second);
      ans=min(ans,t); 
      }
    }
  printf("%lld %d\n",ans.first,ans.second);
  }
}

C

定义两个三元组a,b间距离d(a,b)=max(a.x-b.x,a.y-b.y,a.z-b.z)-min(a.x-b.x,a.y-b.y,a.z-b.z).
求每两个三元组之间的距离之和.

A=a.xb.x,B=a.yb.y,C=a.zb.z A = a . x − b . x , B = a . y − b . y , C = a . z − b . z .
则有 d(a,b)=|AB|+|BC|+|CA|2 d ( a , b ) = | A − B | + | B − C | + | C − A | 2 .
那么 d(a,b)=|(a.xb.x)(a.yb.y)|+|(a.yb.y)(a.zb.z)|+|(a.zb.z)(a.xb.x)|2= d ( a , b ) = | ( a . x − b . x ) − ( a . y − b . y ) | + | ( a . y − b . y ) − ( a . z − b . z ) | + | ( a . z − b . z ) − ( a . x − b . x ) | 2 =
|(a.xa.y)(b.xb.y)|+|(a.ya.z)(b.yb.z)|+|(a.za.x)(b.zb.x)|2 | ( a . x − a . y ) − ( b . x − b . y ) | + | ( a . y − a . z ) − ( b . y − b . z ) | + | ( a . z − a . x ) − ( b . z − b . x ) | 2
又令 A1=a.xa.y,B1=a.ya.z,C1=a.za.x,A2,B2,C2. A 1 = a . x − a . y , B 1 = a . y − a . z , C 1 = a . z − a . x , A 2 , B 2 , C 2 同 理 .
则原式又等于 |A1A2|+|B1B2|+|C1C2|2 | A 1 − A 2 | + | B 1 − B 2 | + | C 1 − C 2 | 2 .
绝对值还是比较坑爹的,我们不妨把 A,B,C A , B , C 三个数组从小到大排序,对于一个 Ai A i ,我们考虑一下它的贡献.
它前面有 i1 i − 1 个比它小的 A A ,它有i1次的正贡献.同理后面有 ni n − i 次的负贡献.
那么它的总贡献就是 i1(ni)=i×2n1 i − 1 − ( n − i ) = i × 2 − n − 1 次.
B,C B , C 也按照同样的计算相加即可.

#include<cstdio> //Ithea Myse Valgulious
#include<algorithm>
#include<cctype>
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return 0&pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const int yuzu=2e5;
typedef int fuko[yuzu|10];
fuko a,b,c;
int main(){
for (int n,i;n=read();){
  for (i=1;i<=n;++i){
    int x=read(),y=read(),z=read();
    a[i]=x-y,b[i]=y-z,c[i]=z-x;
    }
  sort(a+1,a+n+1);
  sort(b+1,b+n+1);
  sort(c+1,c+n+1);
  ll llx=0;
  for (i=1;i<=n;++i) llx+=1ll*(i*2-n-1)*(a[i]+b[i]+c[i]);
  write(llx>>1),pl;
  }
}

D

给定最多1000个集合,集合里的数最多10000,回答q个询问,每次询问两个数是否在同一个集合里.
数据结构题.这题会 bitset b i t s e t 秒切,不会滚蛋.
b[i] b [ i ] 来维护 i i 这个数字在哪一个集合里.如果在第x个集合, b[i][x]=1 b [ i ] [ x ] = 1 .
询问时把 b[i] and b[j] b [ i ]   a n d   b [ j ] ,如果 and a n d 求出的值里至少有一个 1 1 ,显然i,j在同一个集合里.
操作的话自己学一下 bitset b i t s e t 即可.

#include<cstdio> //Ithea Myse Valgulious
#include<cctype>
#include<set>
#include<bitset>
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const int yuzu=3e4,aoi=1018;
bitset<aoi> b[yuzu];
int main(){
int i,q,n=read();
for (i=1;i<=n;++i){
  for (q=read();q--;){
    b[read()][i]=1;
    }
  }
for (q=read();q--;){
  puts((b[read()]&b[read()]).any()?"Yes":"No"); 
  }
}

E

求不能表示为不超过k个斐波那契数之和的最小数.
打表得答案为 F(2k3)1 F ( 2 ∗ k − 3 ) − 1 .
朴素方法行不通,用矩阵快速幂优化即可.

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return 0&pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const int mod=998244353;
struct juzhen{
ll m[3][3];
void clear(){memset(m,0,sizeof m);}
void clearit(){clear();for (int i=1;i<=2;++i) m[i][i]=1;}
juzhen operator *(const juzhen &b) const{
  int i,j,k;
  juzhen ans;
  ans.clear();
  for (i=1;i<=2;++i){
    for (j=1;j<=2;++j){
      for (k=1;k<=2;++k){
        ans.m[i][j]=(ans.m[i][j]+m[i][k]*b.m[k][j])%mod;  
        }
      } 
    }return ans;
  }
}llx;

juzhen operator ^(juzhen a,ll b){
juzhen c;c.clearit();
for (;b;b>>=1,a=a*a) if (b&1) c=c*a;
return c;
}

int main(){
for (int k;read(k);){
  k=k*2+2;
  llx.clear();
  llx.m[1][1]=llx.m[1][2]=llx.m[2][1]=1;
  llx=llx^k;
  write((llx.m[1][1]+mod-1)%mod),pl;
  }
}

F

有n堆石子.先手一开始能拿前面1或者2堆,如果一个人前一手拿了k堆,后面的人只能拿k或者k+1堆.
当有人不能拿的时候游戏结束,分值为先手拿的石子数-后手拿的石子数.
先手最大化答案,后手最小化答案,求最后的结果.

dp d p .考虑 dp[i][j] d p [ i ] [ j ] 为拿到第 i i 堆石子,上一手拿了j堆石子的最大差.
由于两个人都要使自己拿到的石子总数尽量大,那么两个人的选择是相同的.
则转移方程为dp[i][k]=max(0,sum[i+k]-sum[i-1]-dp[i+k][k],sum[i+k+1]-sum[i-1]-dp[i+k+1][k+1]), sum s u m 是前缀和.
dp[1][1] d p [ 1 ] [ 1 ] 即为答案.

#include<bits/stdc++.h> //Ithea Myse Valgulious
using namespace std;
const int yuzu=2.5e4,inf=2e9;
int n,dp[yuzu|10][280],a[yuzu|10];

int dfs(int now,int k){
if (now>n) return 0;
if (~dp[now][k]) return dp[now][k];
int llx=-inf;
if (now+k-1<=n) llx=max(llx,a[now+k-1]-a[now-1]-dfs(now+k,k));
if (now+k<=n) llx=max(llx,a[now+k]-a[now-1]-dfs(now+k+1,k+1));
return dp[now][k]=llx==-inf?0:llx;
}

int main(){
int t,i;
for (scanf("%d",&t);t--;){
  memset(dp,-1,sizeof dp);
  scanf("%d",&n);
  for (i=1;i<=n;++i) scanf("%d",&a[i]),a[i]+=a[i-1];
  printf("%d\n",dfs(1,1));
  }
}

谢谢大家.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值