高考集训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经典斜率
我放斜率里面了