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
的范围至少可以开到,我们不妨就试试
O(n)
O
(
n
)
以上的算法.
考虑二分答案,判断矩阵中是否有
k−1
k
−
1
个数比它小.
我们来看公式
Aij=i2+105×i+j2−105×j+i×j.
A
i
j
=
i
2
+
10
5
×
i
+
j
2
−
10
5
×
j
+
i
×
j
.
原式
=i2+(105+j)×i+j2−105×j.
=
i
2
+
(
10
5
+
j
)
×
i
+
j
2
−
10
5
×
j
.
转化为了一个以
i
i
为未知数的一元二次函数.
它的对称轴在轴的负半轴,即当
j>0
j
>
0
时,函数值是和
i
i
单调的.
那么对于纵列函数值总是递增的,显然可以对每一列二分答案,看看有多少个比二分出来的小.
这是一个二分套二分,复杂度
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.x−b.x,B=a.y−b.y,C=a.z−b.z
A
=
a
.
x
−
b
.
x
,
B
=
a
.
y
−
b
.
y
,
C
=
a
.
z
−
b
.
z
.
则有
d(a,b)=|A−B|+|B−C|+|C−A|2
d
(
a
,
b
)
=
|
A
−
B
|
+
|
B
−
C
|
+
|
C
−
A
|
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=
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.x−a.y)−(b.x−b.y)|+|(a.y−a.z)−(b.y−b.z)|+|(a.z−a.x)−(b.z−b.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.x−a.y,B1=a.y−a.z,C1=a.z−a.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
同
理
.
则原式又等于
|A1−A2|+|B1−B2|+|C1−C2|2
|
A
1
−
A
2
|
+
|
B
1
−
B
2
|
+
|
C
1
−
C
2
|
2
.
绝对值还是比较坑爹的,我们不妨把
A,B,C
A
,
B
,
C
三个数组从小到大排序,对于一个
Ai
A
i
,我们考虑一下它的贡献.
它前面有
i−1
i
−
1
个比它小的
A
A
,它有次的正贡献.同理后面有
n−i
n
−
i
次的负贡献.
那么它的总贡献就是
i−1−(n−i)=i×2−n−1
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
这个数字在哪一个集合里.如果在第个集合,
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
,显然在同一个集合里.
操作的话自己学一下
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(2∗k−3)−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
堆石子,上一手拿了堆石子的最大差.
由于两个人都要使自己拿到的石子总数尽量大,那么两个人的选择是相同的.
则转移方程为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));
}
}
谢谢大家.