noip模拟55[遗憾]

noip模拟55 solutions

所以你知不知道这是上一届的省选模拟???

T1 Skip

这就是个原题好吧,可是我式子推错了

斜率优化+CDQ分治,CDQ主要是用来处理\(a\)的大小关系

\[\frac{(f_j-\frac{j^2+j}{2})-(f_k-\frac{k^2+k}{2})}{j-k}\ge -i \]

保证\(j>k,a[i]>a[j],a[k]\);

注意括号里的所有东西作为一个坐标,我因为把括号拆开了没有找到这个式子

AC_code
#include<bits/stdc++.h>
#define int long long
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--)
const int N=1e5+5;
const int inf=0x3f3f3f3f3f3f3f3f;
int n;
struct node{
    int x,a,dp,y;
    node(){dp=-inf;}
}sca[N];
bool comp1(node a,node b){return a.x<b.x;}
bool comp2(node a,node b){return a.a==b.a?a.x<b.x:a.a<b.a;}
int sta[N],top,bot,ans[N];
double get(int x,int y){return 1.0*(sca[y].y-sca[x].y)/(sca[y].x-sca[x].x);}
void sol(int l,int r){
    if(l==r)return ;
    int mid=l+r>>1;
    sol(l,mid);
    sort(sca+l,sca+mid+1,comp1);
    sort(sca+mid+1,sca+r+1,comp1);
    int i1=l;top=0;bot=1;
    fo(i,mid+1,r){
        while(i1<=mid&&sca[i1].x<=sca[i].x){
            while(top>bot&&get(sta[top-1],sta[top])<=get(sta[top],i1))top--;
            sta[++top]=i1;i1++;
        }
        while(bot<top&&get(sta[bot],sta[bot+1])>=-sca[i].x)bot++;
        if(bot>top)continue;
        ans[sca[i].x]=sca[i].dp=max(sca[i].dp,sca[sta[bot]].y+sca[i].x*sca[sta[bot]].x-(sca[i].x-1)*sca[i].x/2+sca[i].a);
        sca[i].y=sca[i].dp-sca[i].x*(sca[i].x+1)/2;
    }
    sort(sca+mid+1,sca+r+1,comp2);
    sol(mid+1,r);
}
signed main(){
    freopen("skip.in","r",stdin);
    freopen("skip.out","w",stdout);
    scanf("%lld",&n);memset(ans,-0x3f,sizeof(ans));
    fo(i,1,n)scanf("%lld",&sca[i].a),sca[i].x=i,ans[i]=sca[i].dp=sca[i].a-(i-1)*i/2,
    sca[i].y=sca[i].dp-sca[i].x*(sca[i].x+1)/2;
    sort(sca+1,sca+n+1,comp2);
    sol(1,n);
    fo(i,1,n-1)ans[n]=max(ans[n],ans[i]-(n-i)*(n-i+1)/2);
    printf("%lld",ans[n]);
}

T2 String

直接暴搜行吗,正解就是它

排名\(\le 1e18\),这个只有小于8的时候才可能出现

所以先把前面填满了,搜索后面,16进制压位,

AC_code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int k,tim[26],rst,buc[10],bas;
char ss[1005];
ll c;
map<int,ll> mp[10];
int get(int x){return x?1<<x*4-4:0;}
ll dfs(int l,int s,ll rak,int lst){
    if(mp[lst].find(s)!=mp[lst].end()&&rak>mp[lst][s])return mp[lst][s];
    if(s==rst){
        if(rak==1){puts(ss);exit(0);}
        else return mp[lst][s]=1;
    }
    ll ret=0,res;
    for(int z=bas;z<26;z++){
        if(z+'a'==ss[l-1])continue;
        tim[z]++;buc[tim[z]]++;ss[l]=z+'a';
        if(buc[tim[z]]<=k-tim[z]+1){
            res=dfs(l+1,s+get(tim[z])-get(tim[z]-1),rak,tim[z]);
            ret+=res,rak-=res;
        }
        buc[tim[z]]--;tim[z]--;
    }
    return mp[lst][s]=ret;
}
signed main(){
    freopen("string.in","r",stdin);
    freopen("string.out","w",stdout);
    scanf("%d%lld",&k,&c);
    while(k>8){
        for(int i=1;i<k;i++)putchar(bas+'a'),putchar(bas+'b');
        putchar(bas+'a');k-=2;bas+=2;
    }
    for(int i=1;i<=k;i++)rst|=get(i);
    dfs(0,0,c,0);
    puts("-1");
}

T3 Permutation

主要是一个结论,将\(n+1\)划分为\(m+1\)个数的方案数是\({n}\choose{m}\)

这个自己看看就能懂,然后就按照题解上说的,

\[f[n][k]=g[n][k]+\sum\limits^{n-k+1}_{m=1}f[m][k-1] \]

这个就可以一直递归下去了,

这里可以直接统计贡献,重点说一下

\(g[n][k]\)是第一位不相同时的贡献,因为是从小到大排序所以最后一定是n,下一行一定是x+k

所以这里的贡献就是1+....+n-k所以直接求和就是\({n-k}\choose{2}\)

这个就是\(n-k+1\)个数里选\(3\)个数,

你发现递归下去以后,会出现\(g[m][q]\)这个和上面一样,但是我如果想要到这一步

前面必须要删掉\(n-m\)个数,在这里面选出来\(k-q\)个,所以是\(n-m\)\(k-q\)

最后合起来就是\(n-k\)里面选出\(k-q+2\)个数,但是前面的在m==1的时候不成立特判即可

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--)
const int N=1e6+5;
const int mod=1e9+7;
int n,m,k,jc[N],inv[N],ans;
int ksm(int x,int y){
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return ret;
}
int C(int x,int y){
    if(x<0||y<0||y>x)return 0;
    return jc[x]*inv[y]%mod*inv[x-y]%mod;
}
signed main(){
    freopen("perm.in","r",stdin);
    freopen("perm.out","w",stdout);
    scanf("%lld%lld%lld",&n,&k,&m);
    n-=k-m;k=m;if(k==1){printf("%lld",n-1);return 0;}
    jc[0]=1;fo(i,1,n)jc[i]=jc[i-1]*i%mod;
    inv[0]=1;inv[n]=ksm(jc[n],mod-2);
    fu(i,n-1,1)inv[i]=inv[i+1]*(i+1)%mod;
    fo(i,1,k)ans=(ans+C(n-i,k-i+2))%mod;
    fo(i,2,n)ans=(ans+C(n-i-1,k-2)*((i-1-C(i-1,2)+mod)%mod))%mod;
    printf("%lld",ans);
}

小P的生成树

枚举向量方向,直接统计,数据范围太小,好像方法5更佳,但是我不会

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--)
const int N=55;
const int M=205;
const double pi=acos(-1.0);
int n,m,cnt;
struct EDGE{
    int x,y,r,i;
    double du,mo;
    EDGE(){}
}edg[M];
double xl[M*M],ans;
bool comp(EDGE a,EDGE b){return a.mo>b.mo;}
int fa[N];int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
double kruskal(){
    int x=0,y=0;
    sort(edg+1,edg+m+1,comp);
    fo(i,1,n)fa[i]=i;
    fo(i,1,m){
        int fx=find(edg[i].x),fy=find(edg[i].y);
        if(fx==fy)continue;
        fa[fx]=fy;x+=edg[i].r;y+=edg[i].i;
    }
    return sqrt(1.0*x*x+1.0*y*y);
}
signed main(){
    freopen("mst.in","r",stdin);
    freopen("mst.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,m)scanf("%d%d%d%d",&edg[i].x,&edg[i].y,&edg[i].r,&edg[i].i);
    fo(i,1,m)fo(j,i+1,m){
        double tmp=atan((1.0*edg[i].r-edg[j].r)/(1.0*(edg[j].i-edg[i].i)));
        xl[++cnt]=tmp;xl[++cnt]=tmp+pi;
    }
    sort(xl+1,xl+cnt+1);
    cnt=unique(xl+1,xl+cnt+1)-xl-1;
    fo(i,1,cnt-1){
        double tmp=(xl[i]+xl[i+1])/2.0;
        fo(j,1,m)edg[j].mo=edg[j].r*cos(tmp)+edg[j].i*sin(tmp);
        ans=max(ans,kruskal());
    }
    printf("%.6lf",ans);
}
posted @ 2021-09-18 06:58  fengwu2005  阅读(30)  评论(0编辑  收藏  举报