暑假集訓CSP提高模擬19

_君の名は發表於2024-08-13

A. 數字三角形

沒看到拍列,對著自己造的錯樣例改半天。填數,由上往下都向左下填,可以保證有解

點選檢視程式碼
#include<bits/stdc++.h>
const int maxn=550;
using namespace std;
int a[maxn][maxn],n,flag,cnt,maxx,mi;
struct lsx
{
	int x,id;
	bool operator < (const lsx &a) const
	{
		return x>a.x;
	}
}p[maxn];

void solve(int x,int y,int p)
{
//	cout<<x<<" "<<y<<" "<<cnt<<" "<<p<<endl; 
	if(x<1||x>n||y>x||y<1)return ;
	if(flag==1) return ;	
	a[x][y]=p;
	cnt--;
	if(cnt==0)
	{
		flag=1;
		return;
	}
	if(!a[x][y-1])solve(x,y-1,p);
	if(!a[x+1][y])solve(x+1,y,p);
	if(!a[x-1][y])solve(x-1,y,p);
	if(!a[x][y+1])solve(x,y+1,p);
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) 
	{
		cin>>p[i].x,a[i][i]=p[i].x,p[i].id=i;
		if(p[i].x>maxx)maxx=p[i].x,mi=i;
	}
	for(int i=1;i<=n;i++)
	{
		flag=0;
		cnt=p[i].x;
		solve(p[i].id,p[i].id,p[i].x);
	}
	
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
		{
			cout<<a[i][j]<<" ";
		}
		cout<<'\n';
	}
	
	return 0;
}
/*
4
4 2 1 3
*/

B. 那一天她離我而去

按邊暴搜可以得到 \(n=m\) 的23分,加個剪枝用 \(vector\) 存圖可以得到76分(所以為啥前向星只有37)

考慮如何構成一個環,列舉與1相連的出環兒子和入環兒子,因為編號之間的二進位制肯定有一位不一樣

所以考慮列舉位數,每次將1向這一位是0的連邊,這一位是1的向 \(n+1\) 連邊,跑1- \(n+1\) 的最短路

可以保證每一對都跑到

點選檢視程式碼
#include<bits/stdc++.h>
const int maxn=1e5+10;
using namespace std;
int T,n,m,head[maxn],nxt[maxn],to[maxn],val[maxn],tot;
int vis[10010],dis[10010],a[maxn],v[maxn],cnt;
int f[maxn],t[maxn],V[maxn],sum,ans; 

inline void add(int x,int y,int z)
{
	to[++tot]=y;
	val[tot]=z;
	nxt[tot]=head[x];
	head[x]=tot;
}

priority_queue<pair<int,int> >q;
void lsx(int s)
{
	fill(vis+1,vis+n+2,0);
	fill(dis+1,dis+n+2,1e9);
	dis[s]=0;
	q.push({0,s});
	while(q.size())
	{
		int x=q.top().second;
		q.pop();
		if(vis[x])continue;
		vis[x]=1;
		for(int i=head[x];i;i=nxt[i])
		{
			int y=to[i];
			if(dis[y]>dis[x]+val[i])
			{
				dis[y]=dis[x]+val[i];
				q.push({-dis[y],y});
			}
		}
	}
}


int main()
{
//	freopen("leave.in","r",stdin);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>T;
	while(T--)
	{
		sum=cnt=0;;
		cin>>n>>m;
		for(int i=1,x,y,z;i<=m;i++)
		{
			cin>>x>>y>>z;
			if(x==1||y==1)
			{
				if(x==1)a[++cnt]=y,v[cnt]=z;
				else a[++cnt]=x,v[cnt]=z;
			}
			else
			{
				f[++sum]=x,t[sum]=y,V[sum]=z;
			}
		}
		int len=0;
		for(int i=1;i<=cnt;i++)
		{
			for(int j=20;j>=0;j--)
			{
				if((1<<j)&a[i])
				{
					len=max(len,j);
					break;
				}
			}
		}	
		ans=1e9;
		for(int i=len;i>=0;i--)
		{
			memset(head,0,sizeof head);
			tot=0;
			for(int j=1;j<=cnt;j++)
			{
				if((1<<i)&a[j])add(1,a[j],v[j]);
				else add(a[j],n+1,v[j]);
			}
			for(int j=1;j<=sum;j++)
			{
				add(f[j],t[j],V[j]);
				add(t[j],f[j],V[j]);
			}
			lsx(1);
			ans=min(ans,dis[n+1]);
		}
		cout<<(ans==1e9?-1:ans)<<'\n';
	}
	
	return 0;
}

C. 哪一天她能重回我身邊

抽象思路題,正面牌向背面牌連邊權為1的邊,再反向連一個邊權為0的邊,翻一張牌相當於把邊反向,那就是求最小次數

把每張正面的牌的出度變為1,每個聯通塊獨立求答案,方案數是各聯通塊答案之積,對於每個聯通塊來說,邊數大於點數

肯定有一個點出度大於1,跑換根dp,第一次求向下指的邊權為1的邊個數,讓這些邊都變成向上的即可保證每個點出度都

不大於1,再跑第二遍dp更新其他點,只會更新兩點之間的邊,換根之後原來不用反的現在要反,原來用的現在不用。如果

聯通塊不是樹的話,就找環,然後拆環取兩個方向最小值即可,記得加上拆掉的那邊

點選檢視程式碼
#include<bits/stdc++.h>
#define ll long long
const int maxn=2e5+10;
const int mod=998244353;
using namespace std;
int T,n,m,head[maxn],nxt[maxn<<1],to[maxn<<1],val[maxn<<1],tot;
int a[maxn],s,t,pos,sum;
bool vis[maxn],flag;
ll f[maxn],g[maxn],fi,en,ans1,ans2,num;

void add(int x,int y,int z)
{
	to[++tot]=y;
	val[tot]=z;
	nxt[tot]=head[x];
	head[x]=tot;
}

void pre()
{
	tot=1,flag=0,ans1=0,ans2=1;
	memset(head,0,sizeof head);
	memset(vis,0,sizeof vis);
}
int tt;
void calc(int x)
{
	
	vis[x]=1;
	fi++;
	for(int i=head[x];i;i=nxt[i])
	{
		int y=to[i];
		en++;
		if(vis[y])continue;
		calc(y);
	}
}

void dfs(int x,int fa)
{
	vis[x]=1,f[x]=0;
	for(int i=head[x];i;i=nxt[i])
	{
		int y=to[i];
		if(y==fa)continue;
		if(!vis[y])
		{
			dfs(y,x);
			f[x]+=f[y]+val[i];
		}
		else s=x,t=y,pos=i;
	}
}

void solve(int x,int fa)
{
	a[++sum]=g[x];
	for(int i=head[x];i;i=nxt[i])
	{
		int y=to[i];
		if(y==fa) continue;
		if(i==pos||i==(pos^1))continue;
		g[y]=g[x]+(val[i]?-1:1);
		solve(y,x);
	}
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>T;
	while(T--)
	{
		tt+=10;
		cin>>n;
		pre();
		for(int i=1,x,y;i<=n;i++)
		{
			cin>>x>>y;
			add(x,y,1);
			add(y,x,0);
		} 
		for(int i=1;i<=2*n;i++)
		{
			if(!vis[i])
			{
				fi=en=0;
				calc(i);
				if(en/2>fi)
				{
					flag=1;
					break; 
				}
			}
		}
//		exit(tt);
		if(flag)
		{
			cout<<"-1 -1"<<'\n';
			continue;
		}
		memset(vis,0,sizeof vis);
		for(int i=1;i<=2*n;i++)
		{
			if(!vis[i])
			{
				s=t=pos=-1;num=sum=0;
//				memset(a,0,sizeof a); 
				dfs(i,0);g[i]=f[i];
				solve(i,0);
				if(s==-1)
				{
					sort(a+1,a+1+sum);
					for(int j=1;j<=sum;j++)
					{
						if(a[j]!=a[1])break;
						num++;
					}
					ans1+=a[1];
				}
				else
				{
					pos%=2;
					if(g[s]+pos==g[t]+(pos^1))num=2;
					else num=1;
					ans1+=min(g[s]+pos,g[t]+(pos^1));
				}
				ans2=ans2*num%mod;
			}
		}
		cout<<ans1<<" "<<ans2<<'\n';
	}
	
	return 0;
}

D. 單調區間

這裡直接搬學長題解了,%%%

題解圖片

image

點選檢視程式碼
#include<bits/stdc++.h>
const int inf=0x3f3f3f;
const int maxn=2e5+10;
using namespace std;
int a[maxn],n,f[maxn][2],last;
long long ans=0;
void solve(int l)
{
    f[l][0]=inf;f[l][1]=-inf;
    for(int i=l+1;i<=n;i++)
	{
        int x=-inf,y=inf;
        if(f[i-1][0]!=-inf)
		{
            if(a[i]>a[i-1])x=max(x,f[i-1][0]);
            if(a[i]<f[i-1][0])y=min(y,a[i-1]);
        }
        if(f[i-1][1]!=inf)
		{
            if(a[i]<a[i-1])y=min(y,f[i-1][1]);
            if(a[i]>f[i-1][1])x=max(x,a[i-1]);
        }
        if(f[i][1]==y&&f[i][0]==x)break;
        else
		{	f[i][1]=y;
			f[i][0]=x;
		}
        if(f[i][0]==-inf&&f[i][1]==inf)
		{
			last=i-1;
			break;
		}
    }
    ans+=last-l+1;
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
   	cin>>n;
	last=n;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=n;i>=1;i--)solve(i);
    cout<<ans;
    
    return 0;
}

相關文章