加载中…

返回上一页

2022NOIP A层联测29

下发文件(密码为原 accoders 比赛密码)

总结:

今天也是不在状态. T1 看错题目导致认为是一道特别简单的题(其实也很简单);T2 忘了扩展欧拉定理,其实之前做过一道类似的题的(就那个 ab % p ≠ ab%p 我还熟记于心了属于是);T3 做法假了,导致爆 0; T4 连暴力都锅了.

A

题目说的是求和!别想成子段了!提高组 T1 还是没那么简单的

这道题发现合法的方案非常难求,因此正难则反,求不合法的方案,拿总方案减去它.

那么显然,对于到了一个位置 i,只要其中不同时存在三个后缀和 p + q + rq + rr,那么到当前位置的这一段一定不合法(当然是在前面所有的位置都不合法的情况下).

数据范围很小,只有 6 × 3 = 18,可以状压 dp.

dp[i][S] 表示当前已经进行到了第 i 位,总后缀和集合的状态为 S.

初始状态 dp[0][0] = 1,转移时尝试在每一位放,要注意如果符合了上面说的同时存在的条件,就不能转移.

空间开不下?上面又说了范围只有 18,说范围是 18 是因为后面到 m 的位数是能够随便放的,所以在后面单独统计一下就行了.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define pll pair<ll,ll>
#define maxn 51
#define mod 998244353
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar();while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10);putchar(x%10|'0'); }
ll n,m,p,q,r,dp[maxn][1<<18],U,mx,ans;
inline ll ksm(rll a,rll b) { rll ans=1;a%=mod; for(rll i=b;i;i>>=1) { if(i&1) (ans*=a)%=mod; (a*=a)%=mod; } return ans; }
// vector<ll> g;
// inline void dfs(rll x)
// {
// 	if(x==n)
// 	{
// 		rg bool fl1=0,fl2=0;rll fl3=0;
// 		for(rll i=0;i<n;i++) if(g[i]==p) { fl1=fl2=fl3=0; while(i<n&&g[i]==p) i++,fl1=1; while(i<n&&g[i]==q) i++,fl2=1; while(i<n&&g[i]==r) i++,fl3++; if(fl1&&fl2&&fl3>=2) break; i--; }
// 		if(fl1&fl2&fl3>=2) { for(rll i=0;i<n;i++) write(g[i]),put_;putn;ans++; }
// 		return;
// 	}
// 	for(rll i=1;i<=m;i++) g.emplace_back(i),dfs(x+1),g.pop_back();
// }
int main()
{
	freopen("a.in","r",stdin); freopen("a.out","w",stdout);
	// freopen("out.txt","w",stdout);
	n=read();m=read();p=read();q=read();r=read();U=(1<<p+q+r)-1;
	// dfs(0);write(ans);putn;
	dp[0][0]=1; mx=min(max(p,max(q,r)),m);// 最多需要枚举到哪里
	for(rll i=1;i<=n;i++) for(rll S=0,T;S<=U;S++) if(dp[i-1][S])
	{
		for(rll j=1;j<=mx;j++)
		{
			T=(S<<j)&U|(1<<j-1); if((T&(1<<p+q+r-1))&&(T&(1<<q+r-1))&&(T&(1<<r-1))) continue;
			(dp[i][T]+=dp[i-1][S])%=mod;
		}
		(dp[i][0]+=dp[i-1][S]*max(m-mx,0ll)%mod)%=mod;// 多余的直接算,不用再枚举了
	}
	ans=ksm(m,n); for(rll i=0;i<=U;i++) ans=(ans-dp[n][i]+mod)%mod; write(ans);
	return 0;
}

B

显然答案是 (b - 1) × bn-1.

直接去算显然不行. 那么考虑怎么去算.

一种方法是从后往前把 n 的每一位单独拿出来去求. 按照位数从小到大每次把答案乘以 b 的这个数次方,同时 b 也要乘以 10.

关于正确性,手模一下即可. 关键是考场上还没想出来

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define pll pair<ll,ll>
#define maxn 1000001
#define mod m
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar();while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10);putchar(x%10|'0'); }
ll n,b,m,len,len1,ans;
char s[maxn],s1[maxn];
bool fl;
inline ll ksm(rll a,rll b) { rll ans=1;a%=mod; for(rll i=b;i;i>>=1) { if(i&1) (ans*=a)%=mod; (a*=a)%=mod; } return ans; }
int main()
{
	freopen("b.in","r",stdin); freopen("b.out","w",stdout);
	// freopen("in.txt","r",stdin);
	scanf("%s",s+1);scanf("%s",s1+1); m=read(); len=strlen(s+1); len1=strlen(s1+1);
	s[len]--; if(s[len]<'0') { s[len]+=10; for(rll i=len-1;i;i--) { s[i]--; if(s[i]>='0') break; else s[i]+=10; } }
	// puts(s+1);
	// for(rll i=len;i;i--) { s[i]--; if(fl) fl=0,s[i]--; if(s[i]<'0') s[i]+=10,fl=1; putchar(s[i]);}putn;
	for(rll i=1;i<=len1;i++) b=((b<<3)+(b<<1)+(s1[i]^'0'))%mod;
	ans=b-1; for(rll i=len;i;i--) (ans*=ksm(b,s[i]^'0'))%=mod,b=ksm(b,10); write(ans);
	return 0;
}

C

一个显然结论:在整个最优的路径里,从 uv 的路径和从 vw 的路径一定是有中间相交的一部分(包括一个点).

先拿最短路求出 u , v , w 分别到其它点的距离,然后枚举每个点,将它与 v 拼起来作为中间相交的一部分,直接跑一遍取一个最小值就行了,注意相交的这一部分要加两次.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define pll pair<ll,ll>
#define maxn 200001
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar();while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10);putchar(x%10|'0'); }
ll n,m,u,v,w,a[maxn],d[3][maxn],ans=0x3f3f3f3f3f3f3f3f;
vector<ll> g[maxn];
bool fl[maxn];
priority_queue<pll,vector<pll>,greater<pll> > q;
inline void dij(rll x,rll op)
{
	memset(d[op],0x3f,sizeof(d[op])); q.push((pll) { d[op][x]=0,x }); fl[x]=1;
	while(!q.empty())
	{
		rll t=q.top().second;q.pop();fl[t]=0;
		for(rll i=0;i<g[t].size();i++)
		{
			rll to=g[t][i]; if(d[op][to]>d[op][t]+1) { d[op][to]=d[op][t]+1; if(!fl[to]) fl[to]=1,q.push((pll) { d[op][to],to }); }
		}
	}
}
int main()
{
	freopen("c.in","r",stdin); freopen("c.out","w",stdout);
	// freopen("in.txt","r",stdin);
	n=read();m=read();u=read();v=read();w=read();
	for(rll i=1,b,c;i<=m;i++) g[b=read()].emplace_back(c=read()),g[c].emplace_back(b),a[i]=read();
	sort(a+1,a+m+1); for(rll i=2;i<=m;i++) a[i]+=a[i-1]; dij(u,0);dij(v,1);dij(w,2);
	for(rll i=1;i<=n;i++) if(d[0][i]+d[1][i]+d[2][i]<=m) ans=min(ans,a[d[0][i]+d[1][i]+d[2][i]]+a[d[1][i]]); write(ans);
	return 0;
}

D

无论是手画还是用欧拉回路解释,都能够发现一个性质:对于每一个联通块,如果 m 是偶数,则无论如何都能够一笔画出;否则,需要画的笔数为度数为奇数的点的数量除以 2.

举个例子,对于下面这三种情况:

显然对于 1、3 是能够一笔画出的,因为只有基础的两个奇数点. 而 2 有四个,所以需要两笔.

那么最难的实现其实就是求联通块了. 可以先离散化,记录一下每个点的上下左右是否有边,然后对于每个联通块搜索,统计答案.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define pll pair<ll,ll>
#define maxn 2001
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar();while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10);putchar(x%10|'0'); }
ll dx[4][2]={ { 0,-1 },{ 0,1 },{ -1,0 },{ 1,0 } };
ll n,m,ans,x1[maxn],Y1[maxn],x2[maxn],y2[maxn],x[maxn<<1],y[maxn<<1],lenx,leny;
bool mp[maxn<<1][maxn<<1][4],vis[maxn<<1][maxn<<1];
inline ll dfs(rll x,rll y)
{
	rll ans=0,t=0;vis[x][y]=1; for(rll i=0;i<4;i++)
	{
		rll nx=x+dx[i][0],ny=y+dx[i][1]; if((!nx)||nx>lenx||(!ny)||ny>leny||vis[nx][ny]||(!mp[x][y][i])) continue;
		ans+=dfs(nx,ny);
	}
	for(rll i=0;i<4;i++) t+=mp[x][y][i]; return ans+(t&1);
}
int main()
{
	freopen("d.in","r",stdin); freopen("d.out","w",stdout);
	n=read();m=read(); for(rll i=1;i<=n;i++)
	{
		x1[i]=read(),Y1[i]=read(),x2[i]=read(),y2[i]=read();
		if(x1[i]>x2[i]||Y1[i]>y2[i]) swap(x1[i],x2[i]),swap(Y1[i],y2[i]);
	}
	for(rll i=1;i<=n;i++) x[(i<<1)-1]=x1[i],x[i<<1]=x2[i],y[(i<<1)-1]=Y1[i],y[i<<1]=y2[i];
	sort(x+1,x+(n<<1)+1);sort(y+1,y+(n<<1)+1);lenx=unique(x+1,x+(n<<1)+1)-x-1;leny=unique(y+1,y+(n<<1)+1)-y-1;
	for(rll i=1;i<=n;i++) x1[i]=lower_bound(x+1,x+lenx+1,x1[i])-x,Y1[i]=lower_bound(y+1,y+leny+1,Y1[i])-y,
		x2[i]=lower_bound(x+1,x+lenx+1,x2[i])-x,y2[i]=lower_bound(y+1,y+leny+1,y2[i])-y;
	for(rll i=1;i<=n;i++) if(x1[i]==x2[i]) 
		{ for(rll j=Y1[i]+1;j<=y2[i];j++) mp[x1[i]][j][0]=1; for(rll j=Y1[i];j<y2[i];j++) mp[x1[i]][j][1]=1; }
		else { for(rll j=x1[i]+1;j<=x2[i];j++) mp[j][Y1[i]][2]=1; for(rll j=x1[i];j<x2[i];j++) mp[j][Y1[i]][3]=1; }
	for(rll i=1;i<=lenx;i++) for(rll j=1;j<=leny;j++) if((!vis[i][j])&&(mp[i][j][0]||mp[i][j][1]||mp[i][j][2]||mp[i][j][3]))
		{ if(!(m&1)) dfs(i,j),ans++; else ans+=max(1ll,dfs(i,j)>>1); }
	write(ans-1);
	return 0;
}

认真做题,认真改题!

posted @ 2022-11-17 18:02  1Liu  阅读(29)  评论(1编辑  收藏  举报