据说是镇海中学出的题目
大体状况
255/300
T1 xor
题目大意
有一个数列,取一个区间,使区间最大值与次大值异或值最大。
分析
P40
暴力
O(n2)
维护最大值与次大值。
P100
这种题目肯定只有两种方法。
枚举最大值,查找次大值。
枚举次大值,查找最大值。
然后显然是后一种好写。
考虑其匹配的是哪个最大值。
用单调栈处理出前一个大于等于其的值。
设其位置为l,当前位置为r。
如果[l,r]范围扩大,这个答案不变或此数不能成为次大值。
如果[l,r]范围缩小,此数会变成最大值。
前后各扫一遍即可。
代码
然后数组少打一个0减少20分。
感觉是十分低级的错误啊。
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define LL long long
#define chkmin(a,b) a=min(a,b)
#define chkmax(a,b) a=max(a,b)
#define M 2000004
void Rd(int &res){
char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
int n,A[M];
struct P40{
void Solve(){
int Ans=0;
REP(i,0,n-1){
int x=max(A[i],A[i+1]),y=min(A[i],A[i+1]);
chkmax(Ans,x^y);
REP(j,i+2,n){
if(A[j]>=x)y=x,x=A[j];
else if(A[j]>y)y=A[j];
chkmax(Ans,x^y);
}
}
printf("%d\n",Ans);
}
}P40;
struct P100{
int Stk[M],Top;
void Solve(){
int Ans=0;
REP(i,0,n){
while(Top && Stk[Top]<A[i])Top--;
if(Top)chkmax(Ans,Stk[Top]^A[i]);
Stk[++Top]=A[i];
}
Top=0;
DREP(i,n-1,-1){
while(Top && Stk[Top]<A[i])Top--;
if(Top)chkmax(Ans,Stk[Top]^A[i]);
Stk[++Top]=A[i];
}
printf("%d\n",Ans);
}
}P100;
int main(){
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
Rd(n);
REP(i,0,n)Rd(A[i]);
if(n<=5000)P40.Solve();
else P100.Solve();
return 0;
}
T2 atom
题目描述
求一棵看起来非常奇怪的树的子树节点个数。
然而边要自己造,点数达到
109
。
分析
P75
那么你就暴力建边暴力DP求一下就好了。
代码实现起来稍微麻烦一些。
P100
因为这个题目的各种性质,
所以剪掉叶节点后点数一下子就变成了
5∗105
以内。
然后叶节点可以排列组合算,
直接DFS或者一次DP处理均可。
代码
这里选择了对每种
n
建树并计算出所有答案。
然而没有DFS跑得快。
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define LL long long
#define chkmin(a,b) a=min(a,b)
#define chkmax(a,b) a=max(a,b)
int T,n,Case;
char C[14];
int Base[]={0,1,10,100,1000,10000,100000,1000000,10000000,100000000,404};
int Fac[]={1,1,2,6,24,120,720,5040,40320,362880,3628800};
struct P100{
static const int M=50004;
map<int,int>ID[14];
bool Do[14];
int Re[M],Sz[14][M];
int Next[M],To[M],Head[M],Tim[M],id,tot,tar;
void Add_Edge(int u,int v){
if(Tim[u]!=tar)Tim[u]=tar,Head[u]=0;
Next[++tot]=Head[u],To[Head[u]=tot]=v;
}
#define LREP(i,A) for(int i=Head[A];i;i=Next[i])
void Clear(){
tot=id=0;
tar++;
}
void DFS(int A){
Sz[n][A]=1;
if(Tim[A]==tar)LREP(i,A)
DFS(To[i]),Sz[n][A]+=Sz[n][To[i]];
if(Sz[n][A]==1){
int p=Re[A],k=Fac[n],zero=n;
if(p==1)return;
REP(i,0,n){
k/=Fac[p%10];
zero-=p%10;
p/=10;
}
k/=Fac[zero];
Sz[n][A]+=k;
}
}
int A[14];
void Build(int pos,int x,int S,bool k){
if(pos>n){
if(!S)return;
if(k){
int y=0;
REP(i,1,n+1)
y+=Base[A[i]];
if(y!=x)Add_Edge(ID[n][y],ID[n][x]);
}
else ID[n][x]=++id,Re[id]=x;
return;
}
REP(i,0,(n-S)+1)
A[pos]=i,Build(pos+1,x+Base[pos]*i,S+i,k);
}
void Init(){
if(Do[n])return;
Clear();
Build(1,0,0,0);
Build(1,0,0,1);
DFS(ID[n][1]);
Sz[n][ID[n][0]=0]=0;
Do[n]=1;
}
void Solve(){
Init();
int x=0,j=0;
REP(i,0,n){
if(C[i]-'0'>n){printf("Case #%d: %d\n",++Case,0);return;}
j+=C[i]-'0';
x+=Base[i+1]*(C[i]-'0');
}
printf("Case #%d: %d\n",++Case,j>n?1:Sz[n][ID[n][x]]);
}
}P100;
int main(){
scanf("%d",&T);
while(T--){
scanf("%s",C);
n=strlen(C);
P100.Solve();
}
return 0;
}
T3 chocolate
分析
假的T3。
P30
有一个
然后就可以简单贪心。
p==1
时,答案肯定是
n
。
P60
然后是
p≤3
感觉还是能贪心。
先喂%p=0,然后%p=1或%p=2都是需要三个相同才能得到一个收益。
然后它们配对的话两个就可以得到一个收益。
所以算一下就好了。
P100
然后是
p≤4
先想了想枚举一个东西再贪心。
然后发现没有必要。
其实按着上面的思路接着分类讨论一下贪心即可。
具体细节看代码吧。
代码
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define LL long long
#define chkmin(a,b) a=min(a,b)
#define chkmax(a,b) a=max(a,b)
void Rd(int &res){
char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
#define M 10004
int T,n,p,A[M],Case;
struct P30{
int Cnt[4];
void Solve(){
memset(Cnt,0,sizeof(Cnt));
REP(i,0,n)Cnt[A[i]&1]++;
int Ans=Cnt[0]+(Cnt[1]>>1)+(Cnt[1]&1);
printf("Case #%d: %d\n",++Case,Ans);
}
}P30;
struct P60{
int Cnt[4];
void Solve(){
memset(Cnt,0,sizeof(Cnt));
REP(i,0,n)Cnt[A[i]%3]++;
int Ans=min(Cnt[1],Cnt[2]);
Cnt[1]-=Ans;
Cnt[2]-=Ans;
Ans+=(Cnt[1]+3-1)/3;
Ans+=(Cnt[2]+3-1)/3;
Ans+=Cnt[0];
printf("Case #%d: %d\n",++Case,Ans);
}
}P60;
struct P100{
int Cnt[4];
void Solve(){
memset(Cnt,0,sizeof(Cnt));
REP(i,0,n)Cnt[A[i]%4]++;
int Ans=min(Cnt[1],Cnt[3]);
Cnt[1]-=Ans;
Cnt[3]-=Ans;
int p=Cnt[1]==0?3:1;
Ans+=(Cnt[2]>>1);
Cnt[2]=Cnt[2]&1;
if(Cnt[2]){
if(Cnt[p]>=2){
Ans++,Cnt[p]-=2;
Ans+=(Cnt[p]+4-1)/4;
}
else Ans++;
}
else Ans+=(Cnt[p]+4-1)/4;
Ans+=Cnt[0];
printf("Case #%d: %d\n",++Case,Ans);
}
}P100;
int main(){
freopen("chocolate.in","r",stdin);
freopen("chocolate.out","w",stdout);
Rd(T);
while(T--){
Rd(n),Rd(p);
REP(i,0,n)Rd(A[i]);
if(p==1)printf("Case #%d: %d\n",++Case,n);
else if(p==2)P30.Solve();
else if(p==3)P60.Solve();
else P100.Solve();
}
return 0;
}
总结
十分正常的题目与分数。
时间分配略少了一些,否则应该能写出T2。
T2的P75实现花的时间较多,然而暴力DFS不优化也可以过。
T3是假T3,T1失误。