这次的题目都是什么鬼?
玄学乱搞+肉眼看CODE+倒着搜索?
好吧是我ZZ了
T1 玄学乱搞
由于考场上写的部分分做法忘记讨论n<=2000时的情况,少得了30pts
很容易得到一个基于排序的算法:
-
记录每个数的值以及编号
-
按编号将原数组从大到小排序
-
对于每一个数,用在它之前的编号最大和最小的分别更新ans
对于这个算法的正确性显然。因为之前所有点的值都比它大,所以可以稳定确立下min(h[i],h[j])的值,然后记录编号的方式就OK了
但是这里的数据是1e7,快拍时间复杂度为O(n log2 n)要GG的
所以很自然地联想到基数排序,由于我姿势水平有限,像LowestJN dalao那样高级的基排,所以只会按位比较的那种
极限复杂度为:O(n log10 max{a[i]})
当n=1e7,max{a[i]}=1e9时,总操作数为9e7≈1e8
理论上机子快一点,常数小一点就可以卡过去了
然而我太蒟了所以被卡
这里贴一下Radix_sort的CODE以供参考:
#include<cstdio>
#include<cstring>
const int N=1e7+5;
using namespace std;
typedef long long LL;
struct data
{
int x,id;
}a[N],temp[N];
int cnt[15],n,d,MIN,MAX;
LL ans=-1;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch=tc();
while (ch<'0'||ch>'9') ch=tc();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline int int_max(int a,int b)
{
return a>b?a:b;
}
inline int int_min(int a,int b)
{
return a<b?a:b;
}
inline int abs(int x)
{
return x>0?x:-x;
}
inline LL LL_max(LL a,LL b)
{
return a>b?a:b;
}
inline int getbit(int x)
{
int tot=0;
if (!x) return 1;
while (x) ++tot,x/=10;
return tot;
}
inline void radix_sort(int n)
{
register int i,j;
int radix=1;
for (i=1;i<=d;++i,radix*=10)
{
memset(cnt,0,sizeof(cnt));
for (j=1;j<=n;++j)
++cnt[(a[j].x/radix)%10];
for (j=8;j>=0;--j)
cnt[j]+=cnt[j+1];
for (j=n;j>=1;--j)
temp[cnt[(a[j].x/radix)%10]--]=a[j];
memcpy(a,temp,sizeof(a));
}
}
int main()
{
register int i;
//freopen("A.in","r",stdin); freopen("A.out","w",stdout);
for (read(n),i=1;i<=n;++i)
read(a[i].x),a[i].id=i,d=int_max(d,getbit(a[i].x));
radix_sort(n);
for (MIN=MAX=a[1].id,i=2;i<=n;++i)
ans=LL_max(ans,(LL)int_max(abs(a[i].id-MIN),abs(a[i].id-MAX))*a[i].x),MIN=int_min(MIN,a[i].id),MAX=int_max(MAX,a[i].id);
printf("%lld",ans);
return 0;
}
好吧我们来看标算:
由于我们发现从左往右这种传统的扫描方式难以得出答案,所以我们考虑从两边向中间寻找
设立l,r表示目前两端的区间范围,所以我们可以用:
ans=max(ans,(r-l)*min(h[l],h[r]))
来更新ans
由于min这个操作,为了贪心我们把大的那个数留下来到下一次,即
if (h[l]<h[r]) ++l; else --r;
这样就实现了O(n)的复杂度
CODE
#include<cstdio>
using namespace std;
typedef long long LL;
const int N=1e7+5;
int a[N],l,r,n;
LL ans;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch=tc();
while (ch<'0'||ch>'9') ch=tc();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline LL max(LL a,LL b)
{
return a>b?a:b;
}
inline int min(int a,int b)
{
return a<b?a:b;
}
int main()
{
register int i;
//freopen("A.in","r",stdin); freopen("A.out","w",stdout);
for (read(n),i=1;i<=n;++i)
read(a[i]);
l=1; r=n;
while (l<r)
{
ans=max(ans,(long long)(r-l)*min(a[l],a[r]));
if (a[l]<a[r]) ++l; else --r;
}
printf("%lld",ans);
return 0;
}
T2 肉眼看CODE
我是真的蒙蔽了
首先%%%XZTdalao肉眼看拿了70+pts
还有一群初三dalao直接A了此题(据说有人花了2H在看这个)
不过不知道标算是什么
听他们说有一个比较可行的算法,就是先rand()一组方案,然后看一下得分。然后根据得分来修改算法
不过可能要惊人的脚本使用技术和耐心
话说我只认出了姜度dalao的代码,有些人还认出了楼神,Manchery的
实为佩服,只能说我输了
T3 倒着搜索
这道题其实真的很简单。
我们先写好DFS爆搜,然后剪剪枝
接下来开始打表
然后把枚举的顺序由从小到大改为从大到小即可成功A掉此题(快得飞起)
这也是什么搜索技巧吗?
爆搜CODE
#include<cstdio>
#include<cstdlib>
using namespace std;
int num[55],n,m;
inline void print(void)
{
for (register int i=1;i<=m;++i)
if (!num[i]) putchar('-'); else putchar(num[i]+'A'-1);
exit(0);
}
inline void DFS(int now)
{
if (!now) print();
for (register int i=1;i<=m;++i)
if (!num[i])
{
num[i]=now;
if (i+now+1<=m&&!num[i+now+1]) num[i+now+1]=now,DFS(now-1),num[i+now+1]=0;
if (i-now-1>=1&&!num[i-now-1]) num[i-now-1]=now,DFS(now-1),num[i-now-1]=0;
num[i]=0;
}
}
int main()
{
scanf("%d",&n); m=n<<1|1;
DFS(n);
return 0;
}