NOI模拟10
不得不说,和所有人比比看,我简直是菜到家了
看luogu上的题解,随便拿出一篇来,就说T1T2是签到题,我咋连暴力都签不上嘞...
考试过程中,可以说是完全没有策略,碰上啊这种难死一个都切不掉的时候就整个人都懵逼
我不敢撇下正解去打暴力,也是我没有时间写部分分的原因
可以尝试先把暴力打完再搞正解去,明天试一试这个策略..
T1 树
考场上只想到了指数级做法,但是忽略了一点,这个dp的转移和具体是谁没有关系
所以写dp的时候要学会忽略变量,这样就可以变成多项式复杂度
然后先套上一个容斥,再套上一个容斥,就能切掉了...
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return s*t;
}
const int N=505;
int n,mod,ans;
int c[N][N],dp[N][N][N];
signed main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read();mod=read();
fo(i,0,n){
c[i][0]=1;
fo(j,1,i)c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
// cerr<<c[3][2]<<endl;
fo(i,1,n)dp[1][1][i]=i;
fo(i,1,n-1)fo(j,1,i)fo(k,1,n){
dp[i+1][j][k]=(dp[i+1][j][k]-dp[i][j][k]*j*k*2%mod+mod)%mod;
dp[i+1][j][k-1]=(dp[i+1][j][k-1]+dp[i][j][k]*j*(k-1))%mod;
dp[i+1][j+1][k]=(dp[i+1][j+1][k]+dp[i][j][k]*j*k)%mod;
}
fo(i,1,n-1){
ans=0;
fo(j,0,i)ans=(ans+dp[i][j][1]*j)%mod;
printf("%lld\n",ans);
}
}
T2 众数
于是我一直在想是不是可以用线段树维护,但是众数显然是不可以用线段树维护的
但是线段树可以维护另外一个东西,就是占总个数百分之多少以上的,可以直接记录下前几个,当然这里用不到
所以我们不用想log的复杂度了,这样一看就是根号了,显然是不可以分块的
那就是根号分治了,对于同一种颜色的个数分类
大于的可以直接枚举,到时候枚举和其他颜色的贡献就行,先做个前缀和,这样就可以O(其他颜色数)统计了
那么只剩下小于和小于的了,可以扫描线...
学会算复杂度,这样一个暴力就能变成正解,剪一剪复杂度就对了...
AC_code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return s*t;
}
const int N=2e5+5;
int T,n,sq,a[N],lsh[N],lh;
int mxn[N],ans;
vector<int> vec[N];
int pre[N],fro[N],pos[N];
void sol1(){
fo(i,1,lh)if(vec[i].size()>sq){
fo(j,1,n)pre[j]=pre[j-1]+(a[j]==i);
fo(j,1,lh)if(j!=i){
int mx=0,sz=vec[j].size();
fo(k,0,sz-1){
mx=max(mx,pre[vec[j][k]-1]-k+1);
mxn[i]=max(mxn[i],mx+pre[n]-pre[vec[j][k]]+k);
}
mx=0;
fo(k,0,sz-1){
mxn[j]=max(mxn[j],mx+pre[vec[j][k]-1]-k+sz);
mx=max(mx,k-pre[vec[j][k]]);
}
mxn[j]=max(mxn[j],mx+pre[n]);
}
}
}
int zs[N];
void sol2(){
fo(i,1,n)if(vec[a[i]].size()<=sq){
int sz=vec[a[i]].size();
mxn[a[i]]=max(mxn[a[i]],zs[1]+sz-pos[i]);
for(int j:vec[a[i]]){
if(j==i)break;
mxn[a[i]]=max(mxn[a[i]],zs[j+1]+sz-pos[i]+pos[j]+1);
}
for(int j:vec[a[i]]){
int now=pos[i]-pos[j]+1;
fu(p,j,1){
if(zs[p]<now)zs[p]=now;
else break;
}
if(j==i)break;
}
}
fo(i,1,n)zs[i]=0;
}
signed main(){
freopen("mode.in","r",stdin);
freopen("mode.out","w",stdout);
T=read();
while(T--){
lh=n=read();
fo(i,1,n)lsh[i]=a[i]=read();
sort(lsh+1,lsh+lh+1);lh=unique(lsh+1,lsh+lh+1)-lsh-1;
if(lh<=5)sq=0;else sq=n;
fo(i,1,n)a[i]=lower_bound(lsh+1,lsh+lh+1,a[i])-lsh;
fo(i,1,n){
if(vec[a[i]].size())fro[i]=vec[a[i]].back();
else fro[i]=0;
pos[i]=vec[a[i]].size();
vec[a[i]].push_back(i);
}
sol1();sol2();
fo(i,1,lh)vec[i].clear();
reverse(a+1,a+n+1);
fo(i,1,n){
if(vec[a[i]].size())fro[i]=vec[a[i]].back();
else fro[i]=0;
pos[i]=vec[a[i]].size();
vec[a[i]].push_back(i);
}
sol1();sol2();
fo(i,1,lh)vec[i].clear();
ans=0;
fo(i,1,lh)ans=max(ans,mxn[i]);
printf("%d\n",ans);
fo(i,1,lh)if(mxn[i]==ans)printf("%d\n",lsh[i]);
fo(i,1,lh)mxn[i]=0;
}
return 0;
}
T3 简单题
发现了一个大问题我的tarjan缩圆方树是错的,怪不得我调一年暴力也没有调出来...
没有观察性质,是在原图的基础上修改的,于是我们观察原图形态的特点
发现所有的环是梭子状的,于是缩圆方树,并且分类讨论即可...我没写。。。
QQ:2953174821