2019.8.12 NOIP模拟测试18 反思总结

写个博客总是符合要求的对吧

回来以后第一次悄悄参加考试,昨天全程围观…

然后喜提爆炸120分wwwwwwwww

T1用了全机房最慢的写法,导致改掉死循环T掉的一个点以后还是死活过不了最后一个点。T2全世界都会DP只有我没做过关路灯也现场推不出来我不如原地爆炸死得干脆一点还非要DFs骗个40分。T3浪费两个小时,考后不甘心地尝试理解题解和std然后死得如烟花一般惨淡。

哇,墨雨笙今天的洛谷运势,大凶。

T1引子:

引子,作用是引出全文,交代背景,balabalabala…=开局爆炸。

已经不是细节爆炸的问题了,我整个代码的处理都很笨,代码不长但跑得奇慢,就是龟兔赛跑里那一粒尘埃。

90pts知道其他人的解法以后懒得改了。

大模拟没什么好说的…注意细节和写法吧。

#include<iostream>
#include<cstdio>
using namespace std;
int n,m,flag,x1,y1,x2,y2,fx;
int a[2010][2010][2],b[2010][2],cnt,p[2010],sum;
char s[2010][2010];
void dfs(int x){
    p[++sum]=x;
    for(int i=1;i<=a[x][0][0];i++){
        int num=0;
        int x3=a[x][i][0],y3=a[x][i][1];
        fx=(s[x3][y3-1]=='|'?1:-1);
        flag=0;
        while(!flag){
            while(s[x3][y3]=='-'){
                y3+=fx;
            }
            while(s[x3][y3]=='|'){
                x3++;
            }
            if(s[x3][y3]=='+'){
                if(s[x3-1][y3]=='|'){
                    if(s[x3][y3-1]=='-'){
                        fx=-1;
                        y3=y3-1;
                    }
                    else{
                        fx=1;
                        y3=y3+1;
                    }
                }
                else{
                    x3++;
                }
            }
            else flag=1;
        }
        int x4=x3,y4=y3;
        while(s[x4][y4]!='+'){
            y4--;
        }
        int y5=y3;
        while(s[x4][y5]!='+'){
            y5++;
        }
        for(int k=x3+1;k<=n;k++){
            for(int l=y4+1;l<=y5-1;l++){
                if('0'<=s[k][l]&&s[k][l]<='9'){
                    int l1=l;
                    num=s[k][l]-'0';
                    while('0'<=s[k][l1+1]&&s[k][l1+1]<='9'){
                        num*=10;
                        num+=s[k][l1+1]-'0';
                        l1++;
                    }
                    break;
                }
            }
            if(num)break;
        }
        dfs(num);
    }
}
int main()
{
//    freopen("2.in","r",stdin);
//    freopen("2.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%s",s[i]+1);
    }
    for(int i=1;i<=n;i++){
        flag=0;
        for(int j=1;j<=m;j++){
            if(s[i][j]=='+'){
                if(!flag&&s[i][j+1]=='-'){
                    x1=i,y1=j;
                    flag=1;
                }
                else if(flag&&s[i][j+1]=='-'){
                    x1=i,y1=j;
                    flag=1;
                } 
                else if(s[i][j+1]!='-'&&flag){
                    cnt=0;
                    x2=i,y2=j;
                    flag=0;
                    while(s[x1+1][y1]=='|'&&s[x1+1][y2]=='|'){
                        x1++;
                        x2++;
                        if(s[x1][y1-1]=='-'){
                            b[++cnt][0]=x1;
                            b[cnt][1]=y1-1;
                        }
                        if(s[x2][y2+1]=='-'){
                            b[++cnt][0]=x2;
                            b[cnt][1]=y2+1;
                        }
                    }
                    if(x1!=i){
                        for(int k=i+1;k<=x1;k++){
                            for(int l=y1+1;l<=y2-1;l++){
                                if('0'<=s[k][l]&&s[k][l]<='9'){
                                    int l1=l;
                                    int num=0;
                                    num=s[k][l]-'0';
                                    while('0'<=s[k][l1+1]&&s[k][l1+1]<='9'){
                                        num*=10;
                                        num+=s[k][l1+1]-'0';
                                        l1++;
                                    }
                                    for(int k=1;k<=cnt;k++){
                                        a[num][k][0]=b[k][0];
                                        a[num][k][1]=b[k][1];
                                        a[num][0][0]++;
                                    }
                            //        printf("  %d
",num);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    dfs(1);
    for(int i=sum;i>=1;i--){
        printf("%d
",p[i]);
    }
    return 0;
}
View Code

T2可爱精灵宝贝:

全世界都会区间DP

哦不对,是我DP不好,数学不好,字符串不好,数据结构也不好…对不起,我是个垃圾,什么都不好。

几乎是对着正解改,然后又去补习了关路灯,好在关路灯自己写出来了。然后疯了一样理解套路和差异,两道题为什么维度不一样循环层数不一样。

关路灯只用考虑路程和全局除了一段区间以外的贡献值,精灵宝贝这道题的贡献则要受时间限制,通过时间而不是路程计算…当然和路程也有关系。

这种可以往返策略的最优解,一般都是从一个点向外扩展慢慢推出去,大的范围包括小范围并由其推出。可以用区间DP来做,每一次只多拓展1,小区间最优一定会推出大区间最优。一个区间由另一个区间推过来的时候要考虑怎么转移,发现如果记下了是在小区间的哪个端点就可以轻松转移大区间了,这样也决策了是不是要折返。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,k,m,maxt;
int f[110][110][2010],g[110][110][2010],begin,ans;
struct node{
    int a,b,t;
}c[110]; 
bool cmp(node a,node b){
    if(a.a<b.a)return true;
    else return false;
}
int main(){
    scanf("%d%d%d",&n,&k,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&c[i].a,&c[i].b,&c[i].t);
        maxt=max(maxt,c[i].t);
    }
    c[0].a=k,c[0].b=0,c[0].t=1;
    sort(c,c+m+1,cmp);
    for(int i=0;i<=m;i++){
        if(c[i].a==k&&c[i].t==1){
            begin=i;
            break;
        }
    }
    memset(f,-63,sizeof(f));
    memset(g,-63,sizeof(g));
    f[begin][begin][1]=g[begin][begin][1]=0;
    for(int t=1;t<=maxt;t++){
        for(int i=begin;i>=0;i--){
            for(int j=begin;j<=m;j++){
                if(i>0){
                    f[i-1][j][t+c[i].a-c[i-1].a]=max(f[i-1][j][t+c[i].a-c[i-1].a],f[i][j][t]+(c[i-1].t>=t+c[i].a-c[i-1].a?c[i-1].b:0));
                    f[i-1][j][t+c[j].a-c[i-1].a]=max(f[i-1][j][t+c[j].a-c[i-1].a],g[i][j][t]+(c[i-1].t>=t+c[j].a-c[i-1].a?c[i-1].b:0));
                }
                if(j<m){
                    g[i][j+1][t+c[j+1].a-c[j].a]=max(g[i][j+1][t+c[j+1].a-c[j].a],g[i][j][t]+(c[j+1].t>=t+c[j+1].a-c[j].a?c[j+1].b:0));
                    g[i][j+1][t+c[j+1].a-c[i].a]=max(g[i][j+1][t+c[j+1].a-c[i].a],f[i][j][t]+(c[j+1].t>=t+c[j+1].a-c[i].a?c[j+1].b:0));
                }
            }
        }
    }
    for(int t=1;t<=maxt;t++){
        for(int i=begin;i>=0;i--){
            for(int j=begin;j<=m;j++){
                ans=max(f[i][j][t],ans);
                ans=max(g[i][j][t],ans);
            }
        }
    }
    printf("%d",ans);
    return 0;
}
View Code

听说还可以搜索+神仙剪枝,可惜我不会

T3相互再归的鹅妈妈:

诸君,我喜欢鹅妈妈童谣

浪费了两个小时,推了一堆没什么用的性质,并且没看见n=1以及n=2的20分骗分。最后居然打了个爆炸DP拿了5pts,第一次交还是个mle,快乐得很。发觉自己数学过差抱着死扛到底的心态去钻研题解,最后弄懂了有序情况下任选可重复的i个人异或起来为0的式子是什么意思,然后不负期望死在斯特林反演。

唉我说,浪费时间干什么啊墨雨笙,一开始就该意识到没什么意义并且自己能力不够啊hhh

神仙题,大致说说自己对看懂部分的理解。

w[i]是预处理R从低位到高位,到这一位的数的值。例如R=1101(2),w[1]=1,w[2]=1,w[3]=5,w[4]=13。然后处理出p2[i][j]表示2i的j次方也就是2i*j,处理出pw[i][j]是R这个数到i这一位数值的j次方,也就是w[i]的j次方。

然后从高位往地位扫,遇到这一位为1就像题解说的那样,这一位可以【解放】,在i位之前这个数和R相同,这一位R为1而这个数为0,那么其他的数可以随便填只有唯一一种方案能异或出0【这里其实有点不明白,感性理解了】。然后根据这一位去算g[j](g[j]即选出j个数异或和为0)。如果当前是最高位的1或者j为偶数【这里也有点不明白】,那么枚举k,g[j]+=C[j][k](j个人里选k个)*pw[i+1][k](后面乱填的数有w[i]种选择,k个人)*p2[m-i][j-k-1](i前面有这么多可能的取值,剩余的人【这里依然有疑问】)。

这样糊里糊涂地算出来数字可以重复的g数组以后根据斯特林反演就能推出不允许重复的f数组(f[i]=选出i个人异或和为0切不允许数字重复)。由于题目要求无序方案(选出来的数相同就算同一种方案),目前算出来的是有序方案(选出来的数顺序不同就算不同方案),所以还要除掉一个n!,就是一个排列数。

没有代码,扔个std。感觉我在网上找到的std比下发的std好理解。

#include<iostream>
#include<cstdio>
#include<cstring>
#define M 5000010
#define N 8
#define ll long long
#define up(x,y) x=(x+(y))%mod
using namespace std;
const int mod=1000000007;
int n,m,K,w[M],p2[M][N],pw[M][N];
ll g[N],C[N][N],f[N],fac[N],ans;
char s[50010];
bool a[M];
ll ksm(ll a,ll b)
{
    ll r=1;
    for(;b;b>>=1,a=a*a%mod)
        if(b&1) r=r*a%mod;
    return r;   
}
void find(int num,int v)
{
    if(v>n)
    {
        ll c=1,cnt=0;
        for(int i=1;i<=num;i++)
            c=c*fac[f[i]-1]*((f[i]&1)?1:-1)%mod;
        for(int i=1;i<=num;i++)
            if(f[i]&1) cnt++;
            else c=c*w[1]%mod;
        up(ans,c*g[cnt]);   
        return ;
    }
    for(int i=1;i<=num+1;i++)
        f[i]++,find(max(num,i),v+1),f[i]--;
}
int main()
{
    scanf("%d%d%s",&n,&K,s+1);
    m=strlen(s+1);
    for(int i=0;i<K;i++)
        for(int j=1;j<=m;j++)
            a[i*m+j]=s[j]-'0';
    m*=K;
    for(int i=m,mi=1;i;i--,mi=(mi<<1)%mod)
        w[i]=(w[i+1]+mi*a[i])%mod;
    fac[0]=1;
    for(int i=1;i<=n;i++)
        fac[i]=fac[i-1]*i%mod;  
    C[0][0]=1;
    for(int i=1;i<=n;C[i][0]=1,i++)
        for(int j=1;j<=i;j++)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;                        
    bool flag=0;
    g[0]=1;
    for(int i=0,tmp=1;i<=m+1;i++,tmp=(tmp<<1)%mod)
    {
        p2[i][0]=pw[i][0]=1;
        for(int j=1;j<=n;j++)
            p2[i][j]=(ll)tmp*p2[i][j-1]%mod,pw[i][j]=(ll)w[i]*pw[i][j-1]%mod;
    }
    for(int i=1;i<=m;flag|=a[i],i++)
        if(a[i])
        for(int j=1;j<=n;j++)
            if(!flag||!(j&1))
                for(int k=0;k<=((j-1)>>1);k++)
                    up(g[j],C[j][k<<1]*pw[i+1][k<<1]%mod*p2[m-i][j-(k<<1)-1]);

    find(0,1);
    printf("%lld",(ans+mod)*ksm(fac[n],mod-2)%mod);                 

    return 0;
}
View Code

又是日常爆炸的一天,我倒是挺明白自己很垃圾并且总是无意义浪费时间折腾没用的东西的hhh我也知道自己很没用,所以甚至不敢再去多问别人什么东西引起厌恶了。感觉再稍微多说两句话,别人就都知道我是个什么玩意儿唯恐避之不及。

虽然那样也挺正常的就是了。

原文地址:https://www.cnblogs.com/chloris/p/11342854.html