2025“钉耙编程”中国大学生算法设计春季联赛(1)

签到

  • 从公告里听说可以使用 C++ 中的 std::cerr 进行输出调试,以后尝试一下
#include <bits/stdc++.h>
using namespace std;
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		string p;
		cin>>n>>p;
		int ans=-1;
		for(int i=1;i<=n;i++)
		{
			string s;
			cin>>s;
			if(s==p)
			{
				ans=i;
			}
		}
		cout<<ans<<"\n";
	}
	return 0;
}

密码

#include <bits/stdc++.h>
using namespace std;
unordered_map<int,int>q;
int i,x,n;
void calc(int u,int v,int w)
{
	if((w-v)%u==0&&(w-v)/u>=0)
	{
		if(i==1||q[(w-v)/u]==i-1)
		{
			q[(w-v)/u]=i;
			if(i==n)
			{
				x=(w-v)/u;
			}
		}
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		q.clear();
		cin>>n;
		for(i=1;i<=n;i++)
		{
			int u,v,w;
			cin>>u>>v>>w;
			calc(u,v,w);
			calc(u,w,v);
			calc(v,u,w);
			calc(v,w,u);
			calc(w,u,v);
			calc(w,v,u);
		}
		cout<<x<<"\n";
	}
	return 0;
}

分配宝藏

  • 这道题做完都进排行榜第一页了。谁说你手速慢,你手速挺快的呀!
#include <bits/stdc++.h>
using namespace std;
const int mod=1000000007;
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		long long n;
		cin>>n;
		if(n%2==1)
		{
			n--;
		}
		cout<<(2+n)*(n/2)/2%mod<<"\n";
	}
	return 0;
}

航线

  • 有了memset*n这种“科技”,以后再也不需要通过for循环进行初始化了
  • 通过vector<vector<int> >t(n+1,vector<int>(m+1))的方法快速开一个动态二维数组
  • 地图大小是1e6级别的,要两两建边就是1e7的级别,带上一个log很可能跑不过最后也确实没跑过,只要稍微多想一下改成对应建边就可以既降低时间复杂度又降低编程复杂度了,为什么不这么做呢?
#include <bits/stdc++.h>
using namespace std;
vector<int>a[400005];
vector<int>c[400005];
long long dis[400005];
typedef pair<long long,int> pii;
priority_queue<pii,vector<pii>,greater<pii> >q;
int n,m;
int id(int i,int j,int o)
{
	return (i-1)*m+j+o*n*m;
}
void add(int u,int v,int w)
{
	a[u].push_back(v);
	c[u].push_back(w);
}
void dijkstra(int s)
{
	while(q.size())
	{
		q.pop();
	}
	for(int i=0;i<=id(n,m,3);i++)
	{
		dis[i]=LLONG_MAX;
	}
	dis[s]=0;
	q.push(pii(0,s));
	while(q.size())
	{
		int n1=q.top().second;
		if(q.top().first!=dis[n1])
		{
			q.pop();
			continue;
		}
		q.pop();
		for(int i=0;i<a[n1].size();i++)
		{
			if(dis[n1]+c[n1][i]<dis[a[n1][i]])
			{
				dis[a[n1][i]]=dis[n1]+c[n1][i];
				q.push(pii(dis[a[n1][i]],a[n1][i]));
			}
		}
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n>>m;
		for(int i=0;i<=id(n,m,3);i++)
		{
			a[i].clear();
			c[i].clear();
		}
		vector<vector<int> >t(n+1,vector<int>(m+1));
		vector<vector<int> >d(n+1,vector<int>(m+1));
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				cin>>t[i][j];
			}
		}
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				cin>>d[i][j];
			}
		}
		add(0,id(1,1,0),t[1][1]);
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				for(int u=0;u<4;u++)
				{
					if(i!=n)
					{
						add(id(i,j,u),id(i+1,j,2),t[i+1][j]+(u!=2)*d[i][j]);
						add(id(i+1,j,u),id(i,j,3),t[i][j]+(u!=3)*d[i+1][j]);
					}
					if(j!=m)
					{
						add(id(i,j,u),id(i,j+1,0),t[i][j+1]+(u!=0)*d[i][j]);
						add(id(i,j+1,u),id(i,j,1),t[i][j]+(u!=1)*d[i][j+1]);
					}
				}
			}
		}
		dijkstra(0);
		if(dis[id(n,m,0)]==LLONG_MAX)
		{
			cout<<dis[id(n,m,2)]<<"\n";
			continue;
		}
		cout<<min(dis[id(n,m,0)]+d[n][m],dis[id(n,m,2)])<<"\n";
	}
	return 0;
}

海浪

  • 预处理出每个点开始的最长海浪🌊,之后离线倒序处理就好
#include <bits/stdc++.h>
using namespace std;
const int mod=1000000007;
int h[100005],w[100005],ans[100005];
struct s1
{
	int l,r,id;
}s[100005];
multiset<int>l;
multiset<int>r;
bool cmp(s1 a,s1 b)
{
	if(a.r!=b.r)
	{
		return a.r>b.r;
	}
	return a.l>b.l;
}
int len(int L,int R)
{
	return R-L+1;
}
struct t1
{
	int l,r;
	int v;
}t[400005];
void build(int p,int l,int r)
{
	t[p].l=l;
	t[p].r=r;
	if(l==r)
	{
		t[p].v=w[l]-l+1;
		return;
	}
	int mid=(l+r)>>1;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	t[p].v=max(t[p*2].v,t[p*2+1].v);
}
int ask(int p,int l,int r)
{
	if(l<=t[p].l&&r>=t[p].r)
	{
		return t[p].v;
	}
	int mid=(t[p].l+t[p].r)>>1;
	int va=0;
	if(l<=mid)
	{
		va=max(va,ask(p*2,l,r));
	}
	if(r>mid)
	{
		va=max(va,ask(p*2+1,l,r));
	}
	return va;
}
int main()
{
	//freopen("data.in","r",stdin);
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		int n,Q;
		cin>>n>>Q;
		for(int i=1;i<=n;i++)
		{
			cin>>h[i];
		}
		for(int i=1;i<=Q;i++)
		{
			cin>>s[i].l>>s[i].r;
			s[i].id=i;
		}
		int p=1,q=1;
		while(p<n)
		{
			while(q<n)
			{
				l.insert(min(h[q],h[q+1]));
				r.insert(max(h[q],h[q+1]));
				q++;
				if(*(--l.end())>=*r.begin())
				{
					q--;
					auto it=l.find(min(h[q],h[q+1]));
					l.erase(it);
					it=r.find(max(h[q],h[q+1]));
					r.erase(it);
					break;
				}
			}
			w[p]=q;
			if(p!=q)
			{
				auto it=l.find(min(h[p],h[p+1]));
				l.erase(it);
				it=r.find(max(h[p],h[p+1]));
				r.erase(it);
				p++;
			}
			else
			{
				q++;
				p++;
			}
		}
		w[n]=n;
		build(1,1,n);
		sort(s+1,s+Q+1,cmp);
		for(int i=1;i<=Q;i++)
		{
			while(p!=0&&w[p]>=s[i].r)
			{
				p--;
			}
			if(p<s[i].l)
			{
				ans[s[i].id]=len(s[i].l,s[i].r);
			}
			else
			{
				ans[s[i].id]=max(len(p+1,s[i].r),ask(1,s[i].l,p));
			}
		}
		long long sum=0;
		for(int i=1;i<=Q;i++)
		{
			sum=(sum+1ll*i*ans[i]%mod)%mod;
		}
		cout<<sum<<endl;
	}
	return 0;
}
  • 静态区间最值没必要写线段树,用st表不仅代码量小,查询复杂度还更低
#include <bits/stdc++.h>
using namespace std;
const int mod=1000000007;
int h[100005],w[100005],ans[100005];
struct s1
{
	int l,r,id;
}s[100005];
multiset<int>l;
multiset<int>r;
bool cmp(s1 a,s1 b)
{
	if(a.r!=b.r)
	{
		return a.r>b.r;
	}
	return a.l>b.l;
}
int len(int L,int R)
{
	return R-L+1;
}
int st[100005][20];
void st_pre(int n)
{
	for(int i=1;i<=n;i++)
	{
		st[i][0]=w[i]-i+1;
	}
	for(int i=1;i<=31-__builtin_clz(n);i++)
	{
		for(int j=1;j+(1<<i)-1<=n;j++)
		{
			st[j][i]=max(st[j][i-1],st[j+(1<<(i-1))][i-1]);
		}
	}
}
int ask(int l,int r)
{
	int k=31-__builtin_clz(r-l+1);
	return max(st[l][k],st[r-(1<<k)+1][k]);
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		int n,Q;
		cin>>n>>Q;
		for(int i=1;i<=n;i++)
		{
			cin>>h[i];
		}
		for(int i=1;i<=Q;i++)
		{
			cin>>s[i].l>>s[i].r;
			s[i].id=i;
		}
		int p=1,q=1;
		while(p<n)
		{
			while(q<n)
			{
				l.insert(min(h[q],h[q+1]));
				r.insert(max(h[q],h[q+1]));
				q++;
				if(*(--l.end())>=*r.begin())
				{
					q--;
					auto it=l.find(min(h[q],h[q+1]));
					l.erase(it);
					it=r.find(max(h[q],h[q+1]));
					r.erase(it);
					break;
				}
			}
			w[p]=q;
			if(p!=q)
			{
				auto it=l.find(min(h[p],h[p+1]));
				l.erase(it);
				it=r.find(max(h[p],h[p+1]));
				r.erase(it);
				p++;
			}
			else
			{
				q++;
				p++;
			}
		}
		w[n]=n;
		st_pre(n);
		sort(s+1,s+Q+1,cmp);
		for(int i=1;i<=Q;i++)
		{
			while(p!=0&&w[p]>=s[i].r)
			{
				p--;
			}
			if(p<s[i].l)
			{
				ans[s[i].id]=len(s[i].l,s[i].r);
			}
			else
			{
				ans[s[i].id]=max(len(p+1,s[i].r),ask(s[i].l,p));
			}
		}
		long long sum=0;
		for(int i=1;i<=Q;i++)
		{
			sum=(sum+1ll*i*ans[i]%mod)%mod;
		}
		cout<<sum<<endl;
	}
	return 0;
}

船长

#include <bits/stdc++.h>
using namespace std;
const int mod=998244353;
int power(int n,int p)
{
	if(p==0)
	{
		return 1;
	}
	long long tmp=power(n,p/2);
	if(p%2==1)
	{
		return tmp*tmp%mod*n%mod;
	}
	return tmp*tmp%mod;
}
int p[100005],h[35],inv[35],cnt[35];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	for(int i=1;i<=30;i++)
	{
		h[i]=(1<<i);
		inv[i]=power(h[i],998244351);
	}
	int T;
	cin>>T;
	while(T--)
	{
		memset(cnt,0,sizeof(cnt));
		int n,k;
		cin>>n>>k;
		for(int i=0;i<=k;i++)
		{
			cin>>p[i];
			p[i]--;
		}
		long long ans=1;
		for(int i=1;i<=k;i++)
		{
			auto check=[i](int x)
			{
				return p[0]/x!=p[i]/x;
			};
			int t=partition_point(h+1,h+30+1,check)-h;
			cnt[t]++;
		}
		for(int i=1;(1<<(i-1))<=n;i++)
		{
			if(cnt[i])
			{
				ans=ans*((1<<(i-1))-cnt[i])%mod*inv[i-1]%mod;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

切割木材

  • 二分取决于单调性,而单调性在翻转序列后仍然成立,因此partition_point应当适用于一切场景下的二分
  • 没想到最后的错误居然是线段树参数里的int没设成long long……所以说,以后凡是涉及到long long的题目,就一律#define int long long+signed main()吧。你看有大牛都在用,虽然OI-wiki上说不推荐这么用,会降低代码的规范性,但是你那么多单字母变量命名就很规范吗?using namespace std就那么规范吗?#define之后再也不用纠结min、max函数强制类型相同的限制了,都统一起来,何尝不是一种规范呢?也不必说什么浪费,要这么说的话,有些变量开short就够了,你为什么还要用int呢?C++既然提供了long long,就是方便你使用的呀。虽然这样做确实会增加常数,可是ACM中没有梯度部分分,只有通过与否。而且这些常数的增加都来源于大数组,普通变量的影响可以忽略不计,借助int和signed表示同一类型的语言特性就可以处理了
#include <bits/stdc++.h>
using namespace std;
#define int long long
int f[100005];
int a[100005],g[1<<20];
struct t1
{
	int l,r;
	int v;
}t[400005];
void build(int p,int l,int r)
{
	t[p].l=l;
	t[p].r=r;
	if(l==r)
	{
		t[p].v=-1000000000;
		return;
	}
	int mid=(l+r)>>1;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	t[p].v=max(t[p*2].v,t[p*2+1].v);
}
void change(int p,int x,int v)
{
	if(t[p].l==t[p].r)
	{
		t[p].v=v;
		return;
	}
	int mid=(t[p].l+t[p].r)>>1;
	if(x<=mid)
	{
		change(p*2,x,v);
	}
	else
	{
		change(p*2+1,x,v);
	}
	t[p].v=max(t[p*2].v,t[p*2+1].v);
}
int ask(int p,int l,int r)
{
	if(l<=t[p].l&&r>=t[p].r)
	{
		return t[p].v;
	}
	int mid=(t[p].l+t[p].r)>>1;
	int va=-1000000000;
	if(l<=mid)
	{
		va=max(va,ask(p*2,l,r));
	}
	if(r>mid)
	{
		va=max(va,ask(p*2+1,l,r));
	}
	return va;
}
int sum[20][100005],h[100005];
typedef pair<int,int> pii;
pii id[25];
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		int n,m;
		cin>>n>>m;
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
			h[i]=i;
		}
		for(int i=0;i<(1<<m);i++)
		{
			cin>>g[i];
		}
		build(1,0,n);
		change(1,0,f[0]);
		for(int i=1;i<=n;i++)
		{
			f[i]=f[i-1]+g[0];
			for(int j=0;j<m;j++)
			{
				sum[j][i]=sum[j][i-1]+((a[i]>>j)&1);
			}
			for(int j=0;j<m;j++)
			{
				int v=((a[i]>>j)&1);
				auto check=[i,j,v](int x)
				{
					return sum[j][i-1]-sum[j][x]!=(v*(i-1-x));
				};
				int p=partition_point(h,h+i-1,check)-h;
				id[j]=pii(p,j);
			}
			sort(id,id+m);
			reverse(id,id+m);

			int p=0,cur=0,la=0,r=i;
			while(p<m&&id[p].first!=0)
			{
				while(p<m-1&&id[p].first==id[p+1].first)
				{
					cur=cur+(1<<id[p].second);
					p++;
				}
				cur=cur+(1<<id[p].second);
				f[i]=max(f[i],ask(1,id[p].first,r-1)+g[la]);
				la=cur;
				r=id[p].first;
				p++;
			}
			f[i]=max(f[i],ask(1,0,r-1)+g[cur]);
			change(1,i,f[i]);
		}
		cout<<f[n]<<endl;
	}
	return 0;
}
  • 如果把题目中的位运算操作改写成(a|b-a&b),就可以通过动态更新候选区间的方法,节约掉1个log的时间复杂度和编程复杂度
#include <bits/stdc++.h>
using namespace std;
#define int long long
int f[100005];
int a[100005],g[1<<20];
typedef tuple<int,int,int> t1;
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		int n,m;
		cin>>n>>m;
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
		}
		for(int i=0;i<(1<<m);i++)
		{
			cin>>g[i];
		}
		vector<t1>cur;
		cur.emplace_back(f[0],(1<<m)-1,0);
		for(int i=1;i<=n;i++)
		{
			f[i]=LLONG_MIN;
			for(auto &[val,sand,sor]:cur)
			{
				sand&=a[i];
				sor|=a[i];
				f[i]=max(f[i],val+g[sor-sand]);
			}
			auto tmp=cur;
			cur.clear();
			for(auto x:tmp)
			{
				if(cur.empty()||get<1>(cur.back())!=get<1>(x)||get<2>(cur.back())!=get<2>(x))
				{
					cur.push_back(x);
				}
				else
				{
					get<0>(cur.back())=max(get<0>(cur.back()),get<0>(x));
				}
			}
			cur.emplace_back(f[i],(1<<m)-1,0);
		}
		cout<<f[n]<<endl;
	}
	return 0;
}

船舱

  • 拉格朗日插值带有一个求逆元的log,可以通过预处理避免
#include <bits/stdc++.h>
#define int long long
using namespace std;
const signed mod=1000000007;
int power(int n,int p)
{
	if(p==0)
	{
		return 1;
	}
	long long tmp=power(n,p/2);
	if(p%2==1)
	{
		return tmp*tmp%mod*n%mod;
	}
	return tmp*tmp%mod;
}
char ch[85][85];
int c[85][85],n;
int det(int l,int r)
{
	int res=1;
	for(int i=l;i<r;i++)
	{
		for(int j=i;j<=r;j++)
		{
			if(c[j][i]!=0)
			{
				if(i!=j)
				{
					swap(c[j],c[i]);
					res=-res;
				}
				break;
			}
		}
		for(int j=i+1;j<=r;j++)
		{
			while(c[j][i]!=0)
			{
				swap(c[j],c[i]);
				res=-res;
				int rate=c[j][i]/c[i][i];
				if(rate!=0)
				{
					for(int k=i;k<=r;k++)
					{
						c[j][k]=(c[j][k]-c[i][k]*rate%mod)%mod;
					}
				}
			}
		}
	}
	for(int i=l;i<=r;i++)
	{
		(res*=c[i][i])%=mod;
	}
	return res;
}
int calc(int p)
{
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(ch[i][j]=='1')
			{
				c[i][j]--;
				c[i][i]++;
			}
			else if(ch[i][j]=='p')
			{
				c[i][j]-=p;
				c[i][i]+=p;
			}
		}
	}
	int res=det(2,n);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			c[i][j]=0;
		}
	}
	return res;
}
int y[85];
int s[85],jc[85];
int t[85],q[85];
void lagrange_pre(int m)
{
	jc[0]=1;
	for(int i=1;i<=m;i++)
	{
		jc[i]=jc[i-1]*i%mod;
	}
	for(int i=1;i<=m;i++)
	{
		q[i]=jc[m-i]*jc[i-1]%mod;
		if((m-i)%2==1)
		{
			q[i]=-q[i];
		}
		q[i]=power(q[i],mod-2);
	}
}
int lagrange(int m,int k)
{
	s[0]=1;
	for(int i=1;i<=m;i++)
	{
		s[i]=s[i-1]*(k-i)%mod;
	}
	t[m+1]=1;
	for(int i=m;i>=1;i--)
	{
		t[i]=t[i+1]*(k-i)%mod;
	}
	int ans=0;
	for(int i=1;i<=m;i++)
	{
		int p=s[i-1]*t[i+1]%mod;
		ans=(ans+y[i]*p%mod*q[i]%mod)%mod;
	}
	return ans;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		int q;
		cin>>n>>q;
		lagrange_pre(n+1);
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				cin>>ch[i][j];
			}
		}
		for(int i=1;i<=n+1;i++)
		{
			y[i]=calc(i);
		}
		int ans=0;
		for(int i=1;i<=q;i++)
		{
			int a,b;
			cin>>a>>b;
			int r=lagrange(n+1,a*power(b,mod-2)%mod);
			(ans+=i*r%mod)%=mod;
		}
		cout<<(ans+mod)%mod<<"\n";
	}
	return 0;
}
posted @ 2025-03-19 16:05  D06  阅读(41)  评论(0)    收藏  举报
//雪花飘落效果