高考集训2

分数:20分

纲要:

1图论,建图连边

2DP,思维为主

3矩阵 找规律,结论

4DP,斜率优化

T1

A. 交通 - 【比赛】2022高考集训2 - 比赛 - 衡中OI (hszxoj.com)

假设一个点的两条出边为 ,我们新建一个图给 连边。如果一个点的两条入边为 ,我们也给
连边。
不难发现新图上每个点度数恰好为二,并且只有偶环。我们的要求事实上就是在新图上选 n个不相邻的
点,于是答案显然是2^(环数) ,直接并查集即可。
就是因为答案要求一个点只能有一个入度出度
所以相连的(相邻)两条边一定不可以同时选择(必须消掉一条)
在每个环里面(一定是偶数个边,奇数有矛盾,从出边入边的身份考虑)
只能选间隔的size/2个点
选哪个都行,可以把所有点都满足
所以答案乘法原理相乘
2^n
(重边编号不同已经考虑在里面了)

const int SIZE=1e5+1000,MOD=998244353;
int n,m;
struct node
{
    int to,nxt;
}e[SIZE<<1];
int tot,head[SIZE];
inline void add(int x,int y)
{
    e[++tot].to=y,e[tot].nxt=head[x],head[x]=tot;
}
int fa[SIZE<<1];
inline int getfa(int x)
{
    //chu("%d:fa:%d\n",x,fa[x]);
    if(fa[x]!=x)return fa[x]=getfa(fa[x]);
    return x;
}
inline void Merge(int x,int y)//x-->y
{
    int xx=getfa(x),yy=getfa(y);
   // chu("%d %d\n",xx,yy);
    fa[xx]=yy;
}
bool vis[SIZE<<1];
vector<int>can[SIZE<<1];
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    n=re();
    m=n<<1;

    _f(i,1,m)//他们没说按照点编号
    {
        int u=re(),v=re();
        add(u,v);
    }//
    _f(i,1,tot)fa[i]=i;
    _f(i,1,n)
    {
        int b1=head[i],b2=e[head[i]].nxt;
        can[e[b1].to].push_back(b1);
        can[e[b2].to].push_back(b2);
        Merge(b1,b2);
      //  chu("merge:%d %d\n",b1,b2);
    }
    _f(i,1,n)
    {
        int b1=can[i][0],b2=can[i][1];
        Merge(b1,b2);
       // chu("merge:%d %d\n",b1,b2);
    }
    //chu("dfd\n");
    int ans=0;
    //chu("tot:%d\n",tot);
    //_f(i,1,tot)chu("fa[%d:%d\n",i,fa[i]);
    _f(i,1,tot)
    {
        //  chu("fdsf\n");
        int fatot=getfa(i);

        if(!vis[fatot])
        {
            vis[fatot]=true;ans++;
        }
    }
    //chu("df\n");
    chu("%d",(1<<ans)%MOD);

    return 0;
}
/**/

T2

B. 冒泡排序 - 【比赛】2022高考集训2 - 比赛 - 衡中OI (hszxoj.com)

简化题目,就是有n-1对约束关系,(ai<bi,ci<di)求排列数使得所有约束关系满足

DP【i】【j】表示前i个交换里第i个交换在第j个进行的方案数

}
const int md=1e9+7;
int n;
int t1[5005],t2[5005],num[5005],st1[5005],st2[5005];
int dp[5005][5005];
void up(int,int,int*);
inline int down(int a, int  s)
{
    if(s==1)
    {
        return t1[a]+st1[a-(a&(-a))];
    }
    else
        return t2[a]+st2[a-(a&(-a))];
}
int main()
{
    freopen("mp.in","r",stdin);
    freopen("mp.out","w",stdout);
    //10  2 0 4 1 5 8 3 6 9 7

    int ret=0;
    n=re();
    _f(i,1,n)
    {
        num[i]=re()+1;
        if(num[i]==i)
        {
            putchar('0');
            return 0;
        }
        else if(num[i]>i)
        {
            up(i,1,t1);//t1存储如果i位置应该比i+1--num[i]-1位置先换,就+1
//这里存的就是连续的,因为假如a>b,a>b+1,a>b+2...
//那么一定是a>b>b+1>b+2,当a和b+1换了,如果先换b+3,后换b+2那显然是不对了(num[a]还没跟过来呢)
            up(num[i]-1,-1,t1);
//a b c num[c]=a
//a>b>c,可以达到交换成功,需要+1的连续区间:a,b(每一个位置代表他和他以后的优先级关系)
//因此差分修改:a,b,c
        }
        else
        {
            up(num[i],1,t2);
            up(i-1,-1,t2);//t2如果i位置应该比num[i]+1--i-1位置先换,就+1
        }
    }
    _f(i,1,n-1)
    {
        st1[i]=down(i,1);
        st2[i]=down(i,2);
       //chu("st1[%d]:%d st2[%d]:%d\n",i,st1[i],i,st2[i]);
        if(st1[i]&&st2[i])
        {
            putchar('0');return 0;
        }
    }
 //   st1[0]=st2[0]=st1[n]=st2[n]=0;//这部显然吗?
    dp[1][1]=1;
    for(int i=2;i<n;++i)
    {
        int j=i-1;
        if(st1[j])//如果说j>j+1,i必须j后面
        {
            for(int k=1;k<=i;k++)
            {
                dp[i][k]=dp[i][k-1]+dp[j][k-1];
                if(dp[i][k]>=md)dp[i][k]-=md;//?
            }
        }
        else if(st2[j])//i必须再j前
        {
            f_(k,j,1)
            {
                dp[i][k]=dp[i][k+1]+dp[j][k];
                if(dp[i][k]>=md)dp[i][k]-=md;
            }
            //正常的应该是
            //for(int k=1;k<=j;k++)//枚举i在哪里放
            //{
            // for(int kk=k+1;kk<=i;kk++)
            //    dp[i][k]+=dp[j][kk];只要kk大于k就行,那么后缀和优化
           // }
        }
        else
        {
            _f(k,1,j)
            {
                 dp[i][1]+=dp[j][k];
                 if(dp[i][1]>=md)dp[i][1]-=md;
            }
            _f(k,1,i)
            {
                dp[i][k]=dp[i][k-1];
            }
        }
    }
    _f(i,1,n-1)
    {
        ret+=dp[n-1][i];
        if(ret>=md)ret-=md;
    }
    chu("%d",ret);
    return 0;
}
void up(int a,int b,int*s)
{
    while(a<=n)
    {
        s[a]+=b;
        a+=(a&(-a));
    }return;
}

T3

找规律,结论题

只要把矩阵的前两行两列删掉

如果合法,那么一定就全0了

#include<bits/stdc++.h>
#define _f(i,a,b)  for(register int i=a;i<=b;++i)

#define f_(i,a,b)  for(register int i=a;i>=b;--i)

#define ll long long

#define chu printf

#define inf 0x7f7f7f7f

using namespace std;
inline int re()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}
const int md=1e9+7;
ll zhen[2000][2000];
int n,m;
struct node
{
    int op,w;ll num;
    node(){}
    node(int x,int y,ll z)
    {
        op=x,w=y,num=z;
    }
}d[5000];
int tot;
bool check()
{
    _f(i,1,n)
    {
        _f(j,1,m)
        if(zhen[i][j])return false;
    }
    return true;
}
int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    n=re(),m=re();
    _f(i,1,n)
    _f(j,1,m)scanf("%lld",&zhen[i][j]);
    _f(i,2,n)
    {
        d[++tot]=node(1,i,-zhen[i][2]+zhen[i-1][1]);
        ll add=-zhen[i][2]+zhen[i-1][1];
        _f(j,1,m)zhen[i][j]+=add;
    }
    d[++tot]=node(3,1-n,-zhen[n][1]);
    zhen[n][1]=0;
    _f(i,3,m)
    {
        d[++tot]=node(2,i,-zhen[2][i]+zhen[1][i-1]);
        ll add=-zhen[2][i]+zhen[1][i-1];
        _f(j,1,n)zhen[j][i]+=add;
    }
    d[++tot]=node(3,m-1,-zhen[1][m]);
    zhen[1][m]=0;
    _f(i,0,n-1-1)
    {
        d[++tot]=node(3,-i,-zhen[1+i][1]);
        ll add=-zhen[1+i][1];
        for(int x=i+1,y=x-i;x<=n&&y<=m;x++,y=x-i)
        {
            zhen[x][y]+=add;
        }
    }
    f_(i,-1,1-m+1)
    {
        d[++tot]=node(3,-i,-zhen[1][1-i]);
        ll add=-zhen[1][1-i];
        for(int x=1,y=x-i;x<=n&&y<=m;x++,y=x-i)
        {
            zhen[x][y]+=add;
        }
    }
    bool is=true;
    if(check())is=true;
    else is=false;
    if(is==false)chu("-1");
    else{
            chu("%d\n",tot);
        _f(i,1,tot)
        {
            chu("%d %d %lld\n",d[i].op,d[i].w,d[i].num);
        }
    }
    return 0;
}


/*
f[a][b]:
第a个交换在前a个数里面第b个进行的方案数

4 5
3 5 10 2 7
2 6 7 1 7
1 8 6 8 4
4 9 3 9 3


3 3
-1 2 -9
-5 -2 -13
2 5 -6
*/

T4经典斜率

我放斜率里面了

posted on 2022-08-12 20:55  HZOI-曹蓉  阅读(150)  评论(0编辑  收藏  举报