20181103

题目

这是$NOIP$模拟赛(---)

T1:

想的太复杂了,开头$1$个小时认为此题不可做,所以到最后也懒得打$O(n)$的方法,只打了$O(n^2)$的东西

我的做法:

先把数据的图建出来,然后查看对于每个列是否没产生矛盾

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#include<algorithm>
using namespace std;
inline int read(){
    int f=1,ans=0;char c;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
int vis[300001],n,has[300001],a[300001],b[300001],c[300001],lb[300001],lc[300001];
struct node{
    int u,v,nex;
}x[1010001];
int ma[300001];
int cnt,head[300001];
void add(int u,int v){
    x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++;
}
int ba[300001],ca[300001],ans;
int bb[101],cc[101],minn=2<<30-1,aa[101],book[101];
void dfs(int pos){
    if(pos>=1&&pos<=n) ma[++ma[0]]=pos;
    for(int i=head[pos];i!=-1;i=x[i].nex){
        if(!vis[x[i].v]){
            vis[x[i].v]=1;
            dfs(x[i].v);
        }
    }
}
void check(){
    int ans=0;
    aa[0]=0,bb[0]=0,cc[0]=0;
    for(int i=1;i<=n;i++){
        if(book[i]==1){
            aa[++aa[0]]=a[i];
            bb[++bb[0]]=b[i];
            cc[++cc[0]]=c[i];
            ans++;
        }
    }
    sort(aa+1,aa+aa[0]+1);
    sort(bb+1,bb+bb[0]+1);
    sort(cc+1,cc+cc[0]+1);
    for(int i=1;i<=aa[0];i++){
        if(aa[i]==bb[i]&&aa[i]==cc[i]&&bb[i]==cc[i]){
            continue;
        }return ;
    }
    minn=min(minn,n-ans);
    return;
}
void dfs1(int pos){
    if(pos==n+1){
        check();return;
    }
    book[pos]=1;
    dfs1(pos+1);
    book[pos]=0;
    dfs1(pos+1);
    book[pos]=0;
}

int main(){
//     freopen("avogadro.in","r",stdin);
//     freopen("avogadro.out","w",stdout);
    memset(head,-1,sizeof(head));
    n=read();
    for(int i=1;i<=n;i++) a[i]=read(),has[a[i]]=i;
    for(int i=1;i<=n;i++) b[i]=read(),lb[i]=has[b[i]],add(i+n,lb[i]);
    for(int i=1;i<=n;i++) c[i]=read(),lc[i]=has[c[i]],add(i+2*n,lc[i]);
    if(n<=10){
        dfs1(1);
        cout<<minn;
        return 0;
    }
    for(int i=1;i<=n;i++) add(i,i+n),add(i,i+2*n);
    for(int i=1;i<=n;i++){
        ma[0]=0;
        memset(ba,0,sizeof(ba)),memset(ca,0,sizeof(ca));
        memset(vis,0,sizeof(vis));
        vis[i]=1;
        dfs(i);
        for(int j=1;j<=ma[0];j++) ba[b[ma[j]]]++;
        for(int j=1;j<=ma[0];j++) ca[c[ma[j]]]++;
        for(int j=1;j<=ma[0];j++){
            if(ba[b[ma[j]]]!=1||ca[c[ma[j]]]!=1){
                ans++;
                break;
            }
        }
    }
    cout<<ans;
    return 0;
}
/*
9
1 3 5 9 8 6 2 4 7
2 1 5 6 4 9 3 4 7
3 5 1 9 8 6 2 8 7
*/
View Code

别人的做法:

垃圾搜索。其实要抓住第一列是不重复的数这一特点去解题。若第$2,3$列有没有出现而在第$1$行出现的时候,则这一列必须删去,有可能又有不存在的数,就再去删除,时间复杂度$O(n)$,因为每个列只会最多删去一次。并且可以去先按照第一行排序,这样就方便查询了。

正确性:因为有小于$0$的就必有$\leq 2$的数,而这就恰好可以去进行搜索了,但是如果去先统计$\leq 2$的点,就会产生冲突,因为不知道该删哪个

T2:

好像以前讲过类似的题,但是却不会写,自己的暴力也还挂了,看到$n \leq 9$想到什么,搜索  状压啊,但在考场上还不会写。

我们设$dp(i,S)$表示已经要决策到第i条边,点的集合划分为$S$($n!$状压),每个点都能在当前的集合中互相到达的概率是多少。

所以分两种情况讨论:
若第i条边是好的,则$dp(i,合并当前的2个集合)=dp(i-1,S)*(1-{p_i})$

若是不好的,则$dp(i,S)=dp(i-1,S)*(p_i)$

主要是怎么去写那个$S$

#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#define rep(i,x,y) for(register int i=(x);i<=(y);i++)
#define dwn(i,x,y) for(register int i=(x);i>=(y);i--)
#define maxn 10
#define maxm 90
#define maxs 363000
using namespace std;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)&&ch!='-')ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    return x*f;
}
void write(int x)
{
    int f=0;char ch[20];
    if(!x){putchar('0'),putchar('\n');return;}
    if(x<0)x=-x,putchar('-');
    while(x)ch[++f]=x%10+'0',x/=10;
    while(f)putchar(ch[f--]);
    putchar('\n');
}
int mul[maxn],tmp[maxn],tmp2[maxn],eu[maxm],ev[maxm],n,m;
long double ew[maxm],p[2][maxs];
int gx(int x,int y){return (x-1)*mul[y-1];}
void bac(int S)
{
    dwn(i,n,1)
    {
        tmp[i]=S/mul[i-1]+1;
        S%=mul[i-1];
    }
}
int gets()
{
    int s=0;
    rep(i,1,n)
    {
        s+=gx(tmp2[i],i);
    }
    return s;
}
/*
int maxs;
void getst(int x)
{
    if(x==10)
    {
        int s=0;
        rep(i,1,9)
        {
            s+=gx(fa[i],i);
        }
        if(yes[s]){cout<<s<<"nooooooo";exit(0);}
    //    else cout<<"+"<<s<<endl;
        bac(s);
        yes[s]=1;
        maxs=max(maxs,s);
        return;
    }
    rep(i,1,x)
    {
        fa[x]=i;
        getst(x+1);
    }
}*/
int main()
{
    freopen("connect.in","r",stdin);
    freopen("connect.out","w",stdout);
    mul[0]=1;
    rep(i,1,9)mul[i]=mul[i-1]*i;
    //getst(1);
    //cout<<maxs;
    n=read(),m=read();
    rep(i,1,m)
    {
        eu[i]=read(),ev[i]=read();
        if(eu[i]>ev[i])swap(eu[i],ev[i]);
        double w;scanf("%lf",&w);
        ew[i]=w;
    }
    rep(i,1,n)tmp2[i]=i;
    int nows=gets();
    p[0][nows]=1.0;
    //cout<<nows<<endl;
    rep(i,1,m)
    {
        //cout<<"i:"<<i<<endl;
        int now=i&1,pre=now^1;
        rep(u,0,nows)p[now][u]=0;
        rep(u,0,nows)
        {
            if(p[pre][u]==0.0000)continue;
            bac(u);//cout<<"tmp:"<<p[i][u]<<endl;
            //rep(j,1,n)cout<<tmp[j]<<" ";cout<<endl;
            int ss=tmp[eu[i]],tt=tmp[ev[i]];
            if(ss==tt){p[now][u]+=p[pre][u];continue;}
            if(ss>tt)swap(ss,tt);
            rep(j,1,n)
            {
                if(tmp[j]==tt)tmp2[j]=ss;
                else tmp2[j]=tmp[j];
            }
            int nxt=gets();
        //    cout<<"nxt:";
            //rep(j,1,n)cout<<tmp2[j]<<" ";
            //cout<<endl;
            p[now][nxt]+=p[pre][u]*(1.0-ew[i]);        
            p[now][u]+=p[pre][u]*ew[i];
            //cout<<p[i][nxt]<<endl;
        }
    }
    double ans=p[m&1][0];
    printf("%.3lf",ans);
    return 0;
}
syf

T3:斜率优化$dp$,但是没有学,所以留坑待补

$score:60+40+40=140$这分数是要完的节奏啊

接下来的是CXM讲课的几道题

T1:

平面上有$n \leq 3 \times 10^5$的店,坐标$\leq 10^5$,点上有能量,且只能往直右或直上走,每走一次需要有$k$的能量($k \leq 10^3$),问最大能量

 我们只要将怎样去快速转移达到就行了,所以我们采取从下到上,从左往右的顺序去$dp$,每一行内的答案可以去记录,每一列的答案可以用个表去更新,记录

T2:

有$n$个与$m$个单词$(\leq 3\times 10^5)$,每个单词$30$个字符,问每个单词的暴力查找次数($you$ $guess$ 这是什么意思)

建一棵$Trie$树,若$m$中的一个单词$p$在$n$中没有出现过,那么答案就很好统计,否则,我们可以都扫描过一遍,然后看一看是$n$中第几个单词,然后在建一棵$Trie$树,维护一下历史版本的答案,每次统计完答案在加入字符串,最后一块输出即可

猜你喜欢

转载自www.cnblogs.com/si-rui-yang/p/9901009.html