思路:这是一道比较玄学的题目。
首先,此题一定是二分答案,且它的上界应为k,而前缀和直接维护仅只有90。
不难发现每个数的方案数应该与它因子有关,
即 x∗y=i ,不妨令 x<y ,那么可以有 x∈[1,sqrt(i)] , y∈[sqrt(i),i] 。
可以简单地打表出x,y的分布会长成这样:
y:i,i−j,i−j,...2,2,2,...,2,1,1,1,1,1...,1 ;
x:1,i/(i−j),i/(i−j),...,i/2,i/2,...,i/2,i,i,i,...,i 。
那么就可以“跳区间”似的找第k大的数,因为当我们知道 x=num ,一定是先知道它的L,那么它的 R=pos[i/(num+1)]−1 。
code:
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)
#define LL long long
#define db double
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,Sz(a))
#define mcp(a,b) memcpy(a,b,Sz(b))
#define INF 0x3f3f3f3f
#define inf 0x7fffffff
#define pb push_back
#define M 1000005
int n,m,q,k;
int A[M];
LL cnt[M];
struct p20{
void solve(){
int cnt=0;
int w=max(n,m);
REP(i,1,n){
REP(j,1,m){
if(i*j>w)break;
A[++cnt]=i*j;
}
}
sort(A+1,A+1+cnt);
while(q--){
scanf("%d",&k);
printf("%d\n",A[k]);
}
}
}p20;
struct p100{
LL check(int x){
int t=sqrt(x);
LL sum=0;
REP(i,1,min(t,n))sum+=x/i;
REP(i,1,min(t,m))sum+=x/i;
return sum-t*t;
}
void solve(){
while(q--){
scanf("%d",&k);
int L=1,R=k,ans;
while(L<=R){
int mid=(L+R)>>1;
if(check(mid)>=k)ans=mid,R=mid-1;
else L=mid+1;
}
printf("%d\n",ans);
}
}
}p100;
int main(){
// freopen("kth.in","r",stdin);
// freopen("kth.out","w",stdout);
cin>>n>>m>>q;
if(n<=1000 && m<=1000)p20.solve();
else p100.solve();
return 0;
}
思路:这是一道十分水的题目,只是考试时没有去想正解,太关心大暴力了…
此题唯一的烦点就在m次询问,且每次标记一个点,而且m的范围蛮大的。
那么我们究其根本,dp的转移只是它的下方和它的下右方,但这层的dp又是从它的上方和它的上左方收集上去的。
也就是说,每一层的转移只与它的上下四个位置有关。
而针对每个标记的 x , y ,我们一定是可以先找出一条原先最优的路,看标记的这个点是否在这条路径上。
若没有,很简单,输出原来路径的最大值;否则,则一定是找一条次优的路径,而这条次优的路径和原路径不同只是在于x层的原路径的点和我们要找的另一个次优的点。
那么就简单了,我们就只需要预处理出每一层的最优路径和次优路径,且标次层最优路径的点和次优路径的点即可。
code:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)
#define LL long long
#define db double
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,Sz(a))
#define mcp(a,b) memcpy(a,b,Sz(b))
#define INF 0x3f3f3f3f
#define inf 0x7fffffff
#define pb push_back
#define N 1005
int n,m;
LL A[N][N];
struct p40{
#define M 55
bool mark[M][M];
LL A[M][M],B[M][M];
LL work(){
mcp(B,A);
DREP(i,n-1,1){
REP(j,1,i){
if(mark[i][j])continue;
if(mark[i+1][j])B[i][j]+=B[i+1][j+1];
else if(mark[i+1][j+1])B[i][j]+=B[i+1][j];
else B[i][j]+=max(B[i+1][j],B[i+1][j+1]);
}
}
return B[1][1];
}
void solve(){
REP(i,1,n)REP(j,1,i)scanf("%lld",&A[i][j]);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
if(x==1 && y==1)puts("-1");
else {
mark[x][y]=1;
printf("%lld\n",work());
mark[x][y]=0;
}
}
}
}p40;
struct p10{
int s;
bool check(){
s=A[1][1];
REP(i,2,n)REP(j,1,i)if(s!=A[i][j])return 0;
return 1;
}
void solve(){
while(m--){
int x,y;
scanf("%d%d",&x,&y);
if(x==1 && y==1)puts("-1");
else printf("%lld\n",1LL*n*s);
}
}
}p10;
struct p20{
bool check(){
REP(i,1,n)REP(j,1,i)if(A[i][j]!=i-j)return 0;
return 1;
}
void solve(){
LL ans=1LL*n*(n-1)/2;
while(m--){
int x,y;
scanf("%d%d",&x,&y);
if(x==1 && y==1)puts("-1");
else {
if(y==1)printf("%lld\n",ans-n+x-1);
else printf("%lld\n",ans);
}
}
}
}p20;
struct p30{
bool check(){
REP(i,1,n)REP(j,1,i)if(A[i][j]!=1LL*i*j)return 0;
return 1;
}
LL sum[N];
LL calc(){
LL res=0;
REP(i,1,n)res+=1LL*i*i,sum[i]=sum[i-1]+i;
return res;
}
void solve(){
LL ans=calc();
while(m--){
int x,y;
scanf("%d%d",&x,&y);
if(x==1 && y==1)puts("-1");
else {
if(x!=y)printf("%lld\n",ans);
else printf("%lld\n",ans-(sum[n]-sum[x-1]));
}
}
}
}p30;
struct p100{
LL dp1[N][N],dp2[N][N];
LL ans[N][2],Id[N][2];//max_id and second max_id
void Init(){
REP(i,1,n)
REP(j,1,i)
dp1[i][j]=max(dp1[i-1][j-1],dp1[i-1][j])+A[i][j];
REP(i,1,n)dp2[n][i]=A[n][i];
DREP(i,n-1,1)
REP(j,1,i)
dp2[i][j]=max(dp2[i+1][j],dp2[i+1][j+1])+A[i][j];
REP(i,1,n){
REP(j,1,i){
LL now=max(dp1[i-1][j-1],dp1[i-1][j])+max(dp2[i+1][j],dp2[i+1][j+1])+A[i][j];
if(now>=ans[i][0]){
ans[i][1]=ans[i][0];
Id[i][1]=Id[i][0];
ans[i][0]=now;
Id[i][0]=j;
}
else if(now>=ans[i][1]){
ans[i][1]=now;
Id[i][1]=j;
}
}
}
}
void solve(){
Init();
while(m--){
int x,y;
scanf("%d%d",&x,&y);
if(x==1 && y==1)puts("-1");
else printf("%lld\n",y==Id[x][0]?ans[x][1]:ans[x][0]);
}
}
}p100;
int main(){
// freopen("num.in","r",stdin);
// freopen("num.out","w",stdout);
cin>>n>>m;
if(n<=50)p40.solve();
else {
REP(i,1,n)REP(j,1,i)scanf("%lld",&A[i][j]);
if(p10.check())p10.solve();//same
else if(p20.check())p20.solve();//i-j
else if(p30.check())p30.solve();// i*j
else p100.solve();
}
return 0;
}
小结:今天的第3题失误太大了…根本没有去想正解,导致看到第3题无脑敲部分分,导致两个部分分的题意看错了…T_T——看完题先想一波暴力,敲完暴力,再花10分钟思考一下正解。