加载中…

返回上一页

2022NOIP A层联测20

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

多项式求根

发现有如下式子:

x^{n+1}+y^{n+1}=(x+y)(x^n+y^n)-xy(x^{n-1}+y^{n-1}) .

这玩意能拿矩阵快速幂来搞.

那么手模一下,不难发现前 n - 1 次快速幂进行就是:\begin{bmatrix}x+y&-xy \ \1&0 \\end{bmatrix}^2.

最后乘上一个矩阵 \begin{bmatrix}x+y&0 \ \2&0 \\end{bmatrix} 即可.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 8005
#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'); }
struct node
{
	ll a[3][3]; inline node() { memset(a,0,sizeof(a)); }
	inline node(rll x) { memset(a,0,sizeof(a)); for(rll i=1;i<=2;i++) a[i][i]=x; }
	inline friend node operator*(rg node a,rg node b)
	{
		rg node c; for(rll i=1;i<=2;i++) for(rll j=1;j<=2;j++) for(rll k=1;k<=2;k++)
			(c.a[i][j]+=a.a[i][k]*b.a[k][j]%mod)%=mod; return c;
	}
	inline friend void operator*=(rg node& a,rg node b) { a=a*b; }
}c,ans;
ll a,b,n;
inline node ksm(rg node a,rll b) { rg node ans=1; for(rll i=b;i;i>>=1) { if(i&1) ans*=a; a*=a; } return ans; }
int main()
{
	freopen("poly.in","r",stdin); freopen("poly.out","w",stdout);
	a=read();b=read();n=read(); c.a[1][1]=a;c.a[1][2]=-b+mod;c.a[2][1]=1;
	c=ksm(c,n-1);write((c.a[1][1]*a%mod+(c.a[1][2]<<1)%mod)%mod);
	return 0;
}

数三角

找性质题.

正难则反是一个很重要的解决方法. 只需要找到每组异色的,最后把这些异色的情况数除以 2(因为重复算了一回),拿总方案数(\frac{n(n-1)(n-2)}{6})减去它即可.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 8005
#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(rg long long x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
namespace GenHelper
{
	unsigned z1,z2,z3,z4,b,u;
	inline unsigned get()
	{
		b=((z1<<6)^z1)>>13; z1=((z1&4294967294U)<<18)^b; b=((z2<<2)^z2)>>27; z2=((z2&4294967288U)<<2)^b;
		b=((z3<<13)^z3)>>21; z3=((z3&4294967280U)<<7)^b; b=((z4<<3)^z4)>>12; z4=((z4&4294967168U)<<13)^b;
		return (z1^z2^z3^z4);
	}
	inline bool read() { while(!u) u=get(); rg bool res=u&1; u>>=1; return res; }
	inline void srand(rll x) { z1=x; z2=(~x)^0x233333333U; z3=x^0x1234598766U; z4=(~x)+51; u=0; }
}
// using namespace GenHelper;
ll n,cnt[2],ans;
bool mp[maxn][maxn];
int main()
{
	freopen("triangle.in","r",stdin); freopen("triangle.out","w",stdout);
	n=read(); GenHelper::srand(read());
	for(rll i=1;i<=n;i++) for(rll j=i+1;j<=n;j++) mp[i][j]=mp[j][i]=GenHelper::read();
	for(rll i=1;i<=n;i++) { cnt[0]=cnt[1]=0; for(rll j=1;j<=n;j++) if(i^j) cnt[mp[i][j]]++; ans+=cnt[0]*cnt[1]; }
	write(n*(n-1)*(n-2)/6-(ans>>1));
	return 0;
}

棋盘染色

也是一类找性质题.

考虑怎么尽可能地让第二种方法被使用得更多(其实严谨来讲这么说是不对的). 那么一定是要去找到一组初解使它们连通.

想到了什么?

最小生成树!

那么就可以利用最小生成树算法去求解,更新 dis 的时候就在这些 id 里面找到一个最小花费的更新.

TLE 一个点(不知道为什么),加一个卡常优化就能过了.

点击查看代码
#pragma GCC opimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define ll int
#define rg register
#define rll rg ll
#define maxn 5001
#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,a,b,c,d,p,tot,ans;
ll mp[maxn][maxn],dis[maxn<<1];
bool fl[maxn<<1];
inline void prim()
{
	memset(dis,0x3f,sizeof(dis));dis[1]=0;
	for(rll i=1;i<=n+m;i++)
	{
		rll mn=0x3f3f3f3f,id=0;
		for(rll j=1;j<=n+m;j++) if((!fl[j])&&dis[j]<mn) mn=dis[j],id=j; ans+=dis[id];fl[id]=1;// cout<<id<<' '<<dis[id]<<endl;
		if(id<=n) { for(rll j=n+1;j<=n+m;j++) if((!fl[j])&&mp[id][j-n]<dis[j]) dis[j]=mp[id][j-n]; }
		else for(rll j=1;j<=n;j++) if((!fl[j])&&mp[j][id-n]<dis[j]) dis[j]=mp[j][id-n];// ,cout<<j<<'*'<<dis[j]<<endl;
	}
}
int main()
{
	freopen("color.in","r",stdin); freopen("color.out","w",stdout);
	n=read();m=read();a=read();b=read();c=read();d=read();p=read();
	for(rll i=1;i<=n;i++) for(rll j=1;j<=m;j++) a=mp[i][j]=((long long)a*a*b+a*c+d)%p;// ,cout<<mp[i][j]<<' ';cout<<endl;
	prim();write(ans);
	return 0;
}

猜数游戏

只改了个部分分.

dp[i][j][k] 表示一段由 i1j0k1 组成时的答案.

这个 01 具体指的是:

a[y] 表示 Alice 想的是 y,她还能再说 a[y] 次谎. 那么在 Bob 每次猜完一个数,那么所有和 Alice 回答冲突a[y]1.

那么发现这里的 a[y] 一定是连续的 1、连续的 0 和连续的 1. 那么下面转移就利用了这个特点.

转移的话可以用一个记搜,枚举 Bob 每次回答的这个 i,再搜索每个的分段(0/11/0)的位置,分 x , y , z 三个断点,每个断点里分左边右边两种情况转移. dp 式子打着麻烦懒得打了,看代码注释吧.

注意边界(x + y + z = 1)的 dp 值是 1.

点击查看代码
#include<bits/stdc++.h>
#define ll int
#define rg register
#define rll rg 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(rg long long x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
ll n;
unordered_map<ll,ll> dp[maxn][maxn];
inline ll dfs(rll x,rll y,rll z)
{
	if(x+y+z==1) return dp[x][y][z]=1; if(dp[x][y][z]) return dp[x][y][z]; dp[x][y][z]=0x7fffffff;
	for(rll i=1;i<=x-(!(y|z));i++) dp[x][y][z]=min(dp[x][y][z],max(dfs(i,0,y),dfs(x-i,y,z))+1);// 断点 x 还是注意边界等于0的情况
	for(rll i=1;i<=y-(!z);i++) dp[x][y][z]=min(dp[x][y][z],max(dfs(x,i,y-i),dfs(i,y-i,z))+1);// 断点 y
	for(rll i=1;i<z;i++) dp[x][y][z]=min(dp[x][y][z],max(dfs(x,y,i),dfs(y,0,z-i))+1);/*断点 z*/ return dp[x][y][z];
}
int main()
{
	freopen("guess.in","r",stdin); freopen("guess.out","w",stdout);
	n=read(); for(rll i=1;i<=n;i++) write(dfs(i-1,n-i+1,0)-1),put_;
	return 0;
}
posted @ 2022-11-04 17:53  1Liu  阅读(27)  评论(0编辑  收藏  举报