2022高考集训2
大悲
A. 交通
发现如果删掉一条边,那么一定不能删,也就是说一定要删,给边打个标记,对没有标记过的边进行“删除”,将与其“绑定”的边一块标记,最后得到的删除次数能求出答案,
code
#include<cstdio>
#include<cstring>
#define rep(i,a,b,c) for(int i=a;i<=b;c)
#define in(a) scanf("%d",&a)
typedef long long ll;
using namespace std;
const int maxn=300005;
const int mod=998244353;
int n,e[maxn][2];
struct node{
int r1,r2,c1,c2;
}d[maxn];
bool flag=0;
bool vis[maxn];
ll qpow(int x,int y){
ll ans=1;
for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)ans=ans*1ll*x%mod;
return ans;
}
void dfs(int x){
vis[x]=1;
int bz=e[x][1];
int y=d[bz].r1==x?d[bz].r2:d[bz].r1;
int t=e[y][0];
int p=d[t].c1==y?d[t].c2:d[t].c1;
if(vis[p])return ;
else dfs(p);
}
int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
in(n);
rep(i,1,n+n,++i){
int u,v;in(u);in(v);
if(d[u].c1)d[u].c2=i;
else d[u].c1=i;
if(d[v].r1)d[v].r2=i;
else d[v].r1=i;
e[i][0]=u;e[i][1]=v;
}
int ans=0;
for(int i=1;i<=n;++i){
if(!vis[d[i].c1]){
dfs(d[i].c1),++ans;
}
if(!vis[d[i].c2]){
dfs(d[i].c2),++ans;
}
}
printf("%lld\n",qpow(2,ans/2));
return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int maxn=500005;
int rem[maxn],n,cnt;
int re[maxn];
bool vis[maxn];
int main(){
// freopen("a.in","w",stdout);
srand(time(NULL));
n=5;
printf("%d\n",n);
for(int i=1;i<=n;++i){
rem[i*2]=rem[i*2-1]=i;
re[i*2]=re[i*2-1]=i;
}
for(int i=1;i<=n+n;++i)swap(re[i],re[rand()%(n+n)+1]);
bool flag=1;
while(flag){
flag=0;
for(int i=1;i<=n+n;++i){
while(re[i]==rem[i]){
swap(re[i],re[rand()%(n+n)+1]);
flag=1;
}
}
}
for(int i=1;i<=n+n+n+n;++i){
int x=rand()%(n+n)+1;
if(vis[x])continue;
vis[x]=1;
printf("%d %d\n",rem[x],re[x]);
}
for(int i=1;i<=n+n;++i){
if(!vis[i])printf("%d %d\n",rem[i],re[i]);
}
}
B. 冒泡排序
一个位置只会操作一次,那么如果,无解,因为换走就换不回来了
同理,如果一个数需要向前/后移动,那么它到目标位置之间的操作就必须有严格的先后顺序
这样,我们就可以搞出一个形如的序列,表示操作的先后顺序,如果转成图,就是一个上不同的拓扑序的数量
好像很复杂?
但是这个拉开是个链
其实还是线性
表示第个点,在拓扑序的方案数
开始只有一个点,它只能在第一个位置(只有一个位置)
考虑和的关系
- 先于
- 晚于
为啥是从开始,而不是呢?因为我们把序列拓展了,新的点是插进原序列的
3 与没有关系
就这样。。。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i,a,b,c) for(int i=a;i<=b;c)
#define in(a) scanf("%d",&a)
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=5005;
int a[maxn],n,xh[maxn];
int dp[maxn][maxn];
bool check(){
rep(i,1,n,++i){
if(a[i]==i)return false;
if(a[i]<i){
if(xh[i]==2&&i!=1&&i!=n)return false;
xh[i]=1;
rep(j,a[i]+1,i-1,++j)
if(xh[j]==1&&j!=1&&j!=n)return false;
else xh[j]=2;
}
if(a[i]>i){
if(xh[i]==1&&i!=1&&i!=n)return false;
xh[i]=2;
rep(j,i+1,a[i]-1,++j)
if(xh[j]==2&&j!=1&&j!=n)return false;
else xh[j]=1;
}
}
return true;
}
void work(){
dp[1][1]=1;
rep(i,2,n-1,++i){
if(xh[i]==1){
rep(j,1,i,++j)dp[i][j]=(dp[i][j-1]+dp[i-1][j-1])%mod;
}
if(xh[i]==2){
for(int j=i;j>=1;--j)dp[i][j]=(dp[i][j+1]+dp[i-1][j])%mod;
}
if(xh[i]==0){
int su=0;
rep(j,1,i,++j)su=(su+dp[i-1][j])%mod;
rep(j,1,i,++j)dp[i][j]=(su-dp[i-1][j]+mod)%mod;
}
}
}
int main(){
freopen("mp.in","r",stdin);
freopen("mp.out","w",stdout);
in(n);
rep(i,1,n,++i)in(a[i]);
rep(i,1,n,++i)++a[i];
if(check()){
work();
int ans=0;
rep(i,1,n,++i)ans=(ans+dp[n-1][i])%mod;
printf("%d\n",ans);
}else printf("0\n");
return 0;
}
C. 矩阵
这种构造操作的题,一定要研究一下大样例,有人模仿样例推出了解法。。。
首先解出前两行和前两列是很简单的
大概这个样子,,,每次清零一个数,不影响已经清零的数
然后我们证明此时的矩阵如果不是全,一定非法
在的矩形中,绿色减红色=蓝色减黄色
那么从左上角开始取,可以不停的确定一个位置为
code
#include<cstdio>
#include<cstring>
#include<vector>
#define rep(i,a,b,c) for(int i=a;i<=b;c)
#define in(a) scanf("%lld",&a)
typedef long long ll;
using namespace std;
#define int long long
const int maxn=1005;
ll n,m,mp[maxn][maxn];
vector<int>v[4],vv[4];
bool work(){
for(int i=m;i>=1;--i){
v[1].push_back(i);
vv[1].push_back(-mp[1][i]);
for(int j=n;j>=1;--j)mp[j][i]-=mp[1][i];
v[3].push_back(i-2);
vv[3].push_back(-mp[2][i]);
ll x=mp[2][i];
int k=i-2;
for(int j=max(1ll,1-k);j<=n;++j)if(j+k<=m)mp[j][j+k]-=x;
}
rep(i,3,n,++i){
v[2].push_back(i);
vv[2].push_back(-mp[i][2]);
ll x=mp[i][2];
rep(j,1,m,++j)mp[i][j]-=x;
v[3].push_back(1-i);
vv[3].push_back(-mp[i][1]);
x=mp[i][1];int k=1-i;
for(int j=max(1ll,1-k);j<=n;++j)if(j+k<=m)mp[j][j+k]-=x;
}
rep(i,1,n,++i)
rep(j,1,m,++j)
if(mp[i][j]!=0)return false;
return true;
}
signed main(){
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
in(n);in(m);
rep(i,1,n,++i)rep(j,1,m,++j)in(mp[i][j]);
if(n==1){printf("%lld\n",m);rep(i,1,m,++i)printf("2 %lld %lld\n",i,-mp[1][i]);}
else {
if(work()){
printf("%lld\n",(int)(v[1].size()+v[2].size()+v[3].size()));
int s=v[1].size();
rep(i,0,s-1,++i)printf("2 %lld %lld\n",v[1][i],vv[1][i]);
s=v[2].size();
rep(i,0,s-1,++i)printf("1 %lld %lld\n",v[2][i],vv[2][i]);
s=v[3].size();
rep(i,0,s-1,++i)printf("3 %lld %lld\n",v[3][i],vv[3][i]);
}else printf("-1\n");
}
return 0;
}
D. 花瓶
斜率优化,。
奇妙,没有单调性的可以排序确定顺序强制满足单调性
题解说的很详细了,就是具体实现时候
先按照前缀和排序,然后枚举的是中间点,这样
然后因为斜率算的时候有分母为零的,所以可以移项变成乘法
需要注意的是入队的时候弹出条件必须写等号,至于为啥,留坑待补
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i,a,b,c) for(ll i=a;i<=b;c)
#define in(a) scanf("%lld",&a)
typedef long long ll;
using namespace std;
const int maxn=5005;
ll n,a[maxn],p[maxn],q[maxn],head,tail;
ll f[maxn][maxn],sum[maxn];
bool cmp(int x,int y){return sum[x]<sum[y];}
signed main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
memset(f,-0x80,sizeof(f));
in(n);
rep(i,1,n,++i)in(a[i]);
rep(i,1,n,++i)sum[i]=sum[i-1]+a[i];
rep(i,0,n,++i)p[i]=i;
sort(p,p+n+1,cmp);
rep(i,1,n,++i)f[i][0]=f[i][i]=0;
rep(i,1,n,++i){
head=1;tail=0;
rep(j,0,n,++j){
if(p[j]>=i)continue;
while(head<tail&&(f[i][p[j]]-f[i][q[tail]])*(sum[q[tail]]-sum[q[tail-1]])>=(f[i][q[tail]]-f[i][q[tail-1]])*(sum[p[j]]-sum[q[tail]]))--tail;
q[++tail]=p[j];
}
for(int j=n;j>=0;--j){
if(p[j]<=i)continue;
while(head<tail&&(f[i][q[head+1]]-f[i][q[head]])>=(sum[p[j]]-sum[i])*(sum[q[head+1]]-sum[q[head]]))++head;
f[p[j]][i]=max(f[p[j]][i],f[i][q[head]]+1ll*(sum[p[j]]-sum[i])*(sum[i]-sum[q[head]]));
}
}
ll ans=0;
rep(i,0,n,++i)ans=max(ans,f[n][i]);
printf("%lld\n",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】