NOIP模拟测试28「阴阳·虎·山洞」

写这几个题解我觉得我就像在按照官方题解抄一样

阴阳

题解

将题目中给的阴阳看作黑色和白色

首先我们观察到最后生成图中某种颜色必须是竖着单调递增或竖着单调递减

类似这样

否则不满足这个条件

但合法染色方案必须满足任意两个同颜色格子之间的格子也必须是该颜色。

然后我们分四种情况统计,

1.黑色居于左侧而且分界点单调不降,

2.黑色居于左侧而且分界点单调不升,

3.白色居于左侧而且分界点单调不降,

4.白色居于左侧而且分界点单调不升。

我们发现这样会算重,

dp然后手动容斥,

1,2会算重左面每列全是黑色

类似于

类似的3,4会算重左面全是白色

那么上面全是黑色我们也会算重(2,3)

上面全是白色我们仍然会算重

手动容斥

题解应该都能看懂,实现稍难(不用说了我码力太弱)

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 1111
const ll mod=1e9+7;
ll can[A][A][10],f[A][A][2],up[A][A],down[A][A];
//can定义为i行从1--j染成B剩下染成W是否合法
char ch[A][A];
ll n,m,flag1,flag2,ans;
int main(){
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++)
        scanf("%s",ch[i]+1);
    for(ll i=1;i<=n;i++){
        ll maxpos=0;
        for(ll j=1;j<=m;j++){
            if(ch[i][j]=='W') break;
            if(ch[i][j]=='B') maxpos=j;
        }
        for(ll j=maxpos+1;j<=m;j++)
            if(ch[i][j]=='B'){
                maxpos=m+1;    break;
            }
        ll now=maxpos;
        while(ch[i][now]!='W'&&now<=m)
            can[i][now][1]=1,now++;
    }
    
    for(ll i=1;i<=n;i++){
        ll maxpos=m+1;
        for(ll j=m;j>=1;j--){
            if(ch[i][j]=='W') break;
            if(ch[i][j]=='B') maxpos=j;
        }
        for(ll j=maxpos-1;j>=1;j--)
            if(ch[i][j]=='B'){
                maxpos=-1; break;
            }
        ll now=maxpos;
        while(ch[i][now]!='W'&&now>=0)
            can[i][m-now+1][2]=1,now--;
    }
    for(ll i=0;i<=m;i++)
        up[0][i]=1,down[0][i]=1;
    for(ll i=1;i<=n;i++){
        for(ll j=0;j<=m;j++){
            if(!can[i][j][1]) continue;
            f[i][j][0]=up[i-1][j];
            f[i][j][1]=down[i-1][j];
//            printf("up[%lld][%lld]=%lld down=%lld
",i-1,j,up[i-1][j],down[i-1][j]);
        }
        for(ll j=0;j<=m;j++)
            up[i][j]=(up[i][j-1]+f[i][j][0])%mod/*,printf("f=%lld up=%lld
",f[i][j][0],up[i][j])*/;
        for(ll j=m;j>=0;j--)
            down[i][j]=(down[i][j+1]+f[i][j][1])%mod;
//        printf("up=%lld down=%lld
",up[n][m],down[n][0]);
    }
/*    for(ll i=1;i<=n;i++,puts("")){
        for(ll j=1;j<=m;j++){
            printf("%lld ",can[i][j][1]);
        }
    }
*/    memset(f,0,sizeof(f));
    ans=(ans+up[n][m]+down[n][0]+mod)%mod;
//    printf("ans=%lld
",ans);
    for(ll i=1;i<=n;i++){
        for(ll j=0;j<=m;j++){
            if(!can[i][j][2]) continue;
            f[i][j][0]=up[i-1][j];
            f[i][j][1]=down[i-1][j];
        }
        for(ll j=0;j<=m;j++)
            up[i][j]=(up[i][j-1]+f[i][j][0])%mod;
        for(ll j=m;j>=0;j--)
            down[i][j]=(down[i][j+1]+f[i][j][1])%mod;
    }
//    printf("up=%lld down=%lld
",up[n][m],down[n][0]);
    ans=(ans+up[n][m]+down[n][0]+mod)%mod;
//    printf("ans=%lld
",ans);
    memset(f,0,sizeof(f));
    //减去左右重复
    for(ll i=1;i<m;i++){
        ll flag=0;
        for(ll j=1;j<=n;j++)
            if(!can[j][i][1]){
                flag=1;break;
            }
        if(!flag) ans--;
    }
    for(ll i=1;i<m;i++){
        ll flag=0;
        for(ll j=1;j<=n;j++)
            if(!can[j][i][2]){
                flag=1;break;
            }
        if(!flag) ans--;
    }
    //减去上下
    //若上面为B下面只能为W
    for(ll i=1;i<n;i++){
        ll flag=0;
        for(ll j=1;j<=i;j++)
            if(!can[j][m][1]){
                flag=1;break;
            }
        for(ll j=i+1;j<=n;j++)
            if(!can[j][0][1]){
                flag=1;break;
            }
        if(flag)continue;
        ans--;
    }
    for(ll i=n;i>=2;i--){
        ll flag=0;
        for(ll j=1;j<=i-1;j++)
            if(!can[j][0][1]){
                flag=1;break;
            }
        for(ll j=i;j<=n;j++)
            if(!can[j][m][1]){
                flag=1;break;
            }
        if(flag)continue;
        ans--;
    }
    for(ll i=1;i<=n;i++){
        if(!can[i][0][1])    flag1=1;
        if(!can[i][m][1])    flag2=1;
    }
    if(!flag1) ans-=3;
    if(!flag2) ans-=3;
    printf("%lld
",(ans+mod)%mod);
}
View Code

题解

我们发现一条性质我们子树内两个点要变成黑色我们同时经过树外的边不会使次数变优(除了直接与lca相连的边)

然后根据这条性质,我们发现我们一定存在一种最优方案使任意两条染色路径互不相交

然后我们进行贪心

首先题目中给的无要求的边我们进行缩边

我们统计到当前子树都已经全部染成黑色了,那么我们只用考虑与它相连就行

拿下面这个图举例(假设全部为白边)

你考虑3时你6的子树都处理完了,然后你遇到两个白色边(3,6)(3,7)你贪心直接染色就行,那么如果最后剩下一个没有染色怎么办,显然我们不用管,我们发现与3相连的边(1,3)这条边是必定被染色的,2本来要和3进行染色让(1,2)(1,3)进行染色,我们现在把这个边拉过来变成(2,8)进行染色就行了

那么依据上文说的,我们只需要让与3相连白边个数(除了父亲与它相连边)/2就行了

推广到所有点统计白边个数/2即可

然后这样做对于根来说会出错,特判一下就AC了(对于根来说没有办法再让自己父亲拉边过来)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
#define R register
#define ll long long
inline ll read()//-------
{
    ll aa=0;char cc=getchar();
    while(cc<'0'||cc>'9') cc=getchar();
    while(cc<='9'&&cc>='0')
        {aa=(aa<<3)+(aa<<1)+(cc^48);cc=getchar();}
    return aa;
}
const int N=1000005;
struct tree{
    int v,last;
}tr[N<<1];
int tot,first[N];
inline void add(int x,int y)
{
    tr[++tot]=(tree){y,first[x]};
    first[x]=tot;return;
}
int n,ans,mp[N],vi[N];
int dfs(int x,int fa)
{
    int sum=0;vi[x]=1;
    for(R int i=first[x],v;i;i=tr[i].last){
        v=tr[i].v;
        if(v==fa)continue;
        dfs(v,x);
        sum^=1;
        if(!sum)++ans;
    }
    return sum;
}
int main()
{
    //freopen("tight.in","r",stdin);
    //freopen("tight.out","w",stdout);
    n=read();mp[1]=++mp[0];
    for(R int i=2,x,y,z;i<=n;++i){
        x=read();y=read();z=read();
        if(!z){
            if(mp[i])mp[x]=mp[i];
            else if(mp[x])mp[i]=mp[x];
            else mp[i]=mp[x]=++mp[0];
            continue;
        }
        if(!mp[i])mp[i]=++mp[0];
        if(!mp[x])mp[x]=++mp[0];
        if(y)continue;
        add(mp[i],mp[x]);
        add(mp[x],mp[i]);
    }
    for(R int i=1;i<=mp[0];++i)
        if(!vi[i])
            if(dfs(i,i))++ans;
    printf("%d",ans);
    return 0;
}
View Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<vector>
#define R register
using namespace std;
inline int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return f*x;
}
const int maxn=1000005;
struct node{
    int v,nxt,da;
}e[2*maxn];int h[maxn],nu;
void add(int x,int y,int fl)
{
    e[++nu].v=y;
    e[nu].da=fl;
    e[nu].nxt=h[x];
    h[x]=nu;
}
int n,f[maxn],ans;
int dfs(int x,int xf)
{
    int cnt=0;
    for(int i=h[x];i;i=e[i].nxt)
    {
        int y=e[i].v,k=e[i].da;
        if(y==xf)continue;
        int nw=dfs(y,x);
        //cout<<x<<" "<<y<<" "<<k<<endl;
        if(k==1){cnt++;continue;}
        if(!nw)continue;
        if(k==3)ans++;
        else cnt++;
    }
    ans+=cnt/2;
    if(cnt%2==0)return 0;
    return 1;
}
int main()
{
    //freopen("data","r",stdin);
    n=read();
    int qj1=1,num1=0;
    for(int x=2;x<=n;++x)
    {
        int y=read(),s=read(),t=read(),fl;
        if(t==0)fl=2;
        if(s==0&&t==1)num1++,fl=1;
        if(s==1&&t==1)fl=3;
        add(x,y,fl);
        add(y,x,fl);
    }
    int nw=dfs(1,0);
    ans+=nw;
    printf("%d
",ans);
}/*
g++ 1.cpp -o 1
./1

*/
View Code
原文地址:https://www.cnblogs.com/znsbc-13/p/11424317.html