2022NOIP A层联测20
下发文件(密码为原 accoders 比赛密码)
多项式求根
发现有如下式子:
.
这玩意能拿矩阵快速幂来搞.
那么手模一下,不难发现前 n - 1 次快速幂进行就是:.
最后乘上一个矩阵 即可.
点击查看代码
#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(因为重复算了一回),拿总方案数()减去它即可.
点击查看代码
#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] 表示一段由 i 个 1、j 个 0、k 个 1 组成时的答案.
这个 0 和 1 具体指的是:
令 a[y] 表示 Alice 想的是 y,她还能再说 a[y] 次谎. 那么在 Bob 每次猜完一个数,那么所有和 Alice 回答冲突的 a[y] 减 1.
那么发现这里的 a[y] 一定是连续的 1、连续的 0 和连续的 1. 那么下面转移就利用了这个特点.
转移的话可以用一个记搜,枚举 Bob 每次回答的这个 i,再搜索每个的分段(0/1 变 1/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;
}
--END--
我的博客: 𝟷𝙻𝚒𝚞
本文链接: https://www.cnblogs.com/1Liu/p/16858635.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!