noip模拟赛Bywzj52501 17.10.18

T1 rob

环形消灭虫子

先想出了一个n^2暴力

然后我们想到 如果从两个连续的点求解 则会出现仅有的两种结果

(因为这两种情况的交是全集)

当时因为Naive求了50次

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define ll long long
using namespace std;
int a[2*152600];
ll dp[2*152600];
int main()
{
    freopen("rob.in","r",stdin);
    freopen("rob.out","w",stdout);
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){scanf("%d",&a[i]);a[i+n]=a[i];}
    ll res=-1;
    int tms=min(n,50);
    for(int i=0;i<tms;i++)
    {
        memset(dp,0,sizeof(dp));
        for(int j=1;j<=n;j++)
        {
            if(j>2)dp[j]=dp[j-2];
            if(j>=3 && dp[j]<dp[j-3])dp[j]=dp[j-3];
            dp[j]+=a[j+i];
        }
        dp[n]=max(dp[n-1],dp[n-2]);
        res=max(res,dp[n]);
    }
    printf("%lld",res);
    return 0;
}
View Code

T2 destroy

有n个点m条边

现在删掉一些边 把图分为两个联通块 求两个联通块中权值之和最小值

我们跑一个最小生成树 再把其中最大的一条边砍掉 就可以保证 剩下的是两个联通块 且权值之和最小

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define ll long long
using namespace std;
inline int read()
{
    char ch=getchar();
    int x=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=10*x+ch-'0';ch=getchar();}
    return x*f;
}
int n,m;
struct edg
{
    int from,to;
    ll val;
    bool operator <(const edg b)const
    {
        return val<b.val;
    }
}es[152600];
int cnt;
int fa[152600];
inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}

inline void add(int u,int v,int w)
{
    es[cnt++]=(edg){u,v,w};
}
ll mx,sm;
void kruskal()
{
    sort(es,es+cnt);
    for(int i=0;i<=cnt;i++)fa[i]=i;
    for(int i=0;i<cnt;i++)
    {
        int fx=find(es[i].from);
        int fy=find(es[i].to);
        if(fx!=fy)
        {
            mx=max(mx,es[i].val);
            sm+=es[i].val;
            fa[fx]=fy;
        }
    }
}

int main()
{
    freopen("destroy.in","r",stdin);
    freopen("destroy.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
    }
    kruskal();
    printf("%lld",sm-mx);
    return 0;
}
View Code

T3 permutation

给定n个数 求这n个数全排列中满足

对所有 2 ≤ i ≤ N - 1,都有 A[i - 1] + A[i + 1] ≥ 2 × A[i]

的方案数

50分 全排列

70分 状压 n^3*2^n

100分 爆搜

爆搜。。。

但是LZJ巨神告诉我们 事情并没有这么简单

首先  满足条件的序列肯定是下凸的单峰型

首先考虑序列

i  j  ... ...  l  k

现在要在这个序列左端插入一个x 只要满足 a[x]-a[j]>=2*a[i]就可以插进来 j l之间是什么数并不影响结果

右端插x同理

于是我们排序 枚举每个数往左插还是往右插

复杂度n^5

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define ll long long
const int MOD=998244353;
using namespace std;
inline int read()
{
    char ch=getchar();
    int x=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=10*x+ch-'0';ch=getchar();}
    return x*f;
}
int a[50];
int n;
ll ans;
int dp[50][50][50][50];
bool vis[50][50][50][50];
int cnt[50];
void dfs(int i,int j,int k,int l)
{
    if(vis[i][j][k][l])return;
    vis[i][j][k][l]=1;
    int mx=max(i,max(j,max(k,l)));
    mx++;
    if(mx==n)
    {
           if((!j||a[j]-a[i]>=a[i]-a[mx]) && (!l||a[l]-a[k]>=a[k]-a[mx]))  (ans+=dp[i][j][k][l])%=MOD;
           return;
    }
    for(int o=0;o<=cnt[mx];o++)
    {
        bool flag1=0,flag2=0;
        if(!j||a[mx]+a[j]>=2*a[i]||!o)flag1=1;
        if(!l||a[mx]+a[l]>=2*a[k]||!(cnt[mx]-o))flag2=1;
        if(flag1 && flag2)
        {
               int ni=i,nj=j,nk=k,nl=l;
               if(o==1)nj=i,ni=mx;
               else if(o>1)nj=ni=mx;
               if(cnt[mx]-o==1)nl=k,nk=mx;
               else if(cnt[mx]-o>1)nl=nk=mx;
               (dp[ni][nj][nk][nl]+=dp[i][j][k][l])%=MOD;
          }
    }
}

int main()
{
    //freopen("permutation.in","r",stdin);
    //freopen("permutation.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)a[i]=-read();
    sort(a+1,a+n+1);
    int tag=0;
    for(int i=1;i<=n;i++)a[i]=-a[i];
    for(int i=1;i<=n;i++)
    {
           if(a[i]==a[tag])cnt[tag]++;
           else
           {
               a[++tag]=a[i];
               cnt[tag]=1;
        }
    }
    n=tag;
    dp[0][0][0][0]=1;
    for(int i=0;i<n;i++)
        for(int j=0;j<=i;j++)
            for(int k=0;k<=i;k++)
                for(int l=0;l<=k;l++)
                {
                    dfs(i,j,k,l);
                    dfs(k,l,i,j);
                }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=cnt[i];j++)(ans*=j)%=MOD;
    cout<<ans;
}
View Code

Orz LZJ

最后得分应该是80+100+50=230

(第一题爆int咯妈了个zz)

但是xgy错了一个价值80分的等号

yyc错了价值...不知道多少分反正好多好多分的三个字符

所以我好像是3个250中考的最高的Orz

posted @ 2017-10-18 21:25  探险家Mr.H  阅读(252)  评论(0编辑  收藏  举报