题目链接
C-牛牛的揠苗助长
二分天数然后三分高度check即可。
#include
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define per(i,a,b) for(int i=a;i>=(b);--i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=1e5+10;
ll a[N],n,b[N];
ll run(ll mid)
{
ll ans=0;
rep(i,1,n) ans+=abs(b[i]-mid);
return ans;
}
int cal(ll mid)
{
ll d=mid%n;
for(ll i=1;i<=n;++i) {
b[i]=a[i]+mid/n;
if(d>=i) b[i]++;
}
ll l=b[1],r=b[1];
rep(i,1,n) l=min(l,b[i]),r=max(r,b[i]);
while(l+10>1;
ll m2=m1+r>>1;
if(run(m1)>=run(m2))l=m1;
else r=m2;
}
for(ll i=l;i<=r;++i) if(run(i)<=mid) return 1;
return 0;
}
void solve()
{
cin>>n;
rep(i,1,n) scanf("%lld",&a[i]);
ll l=1,r=1e15,ans=1;
while(l<=r){
ll mid=l+r>>1;
if(cal(mid)) r=mid-1,ans=mid;
else l=mid+1;
}
cout<
D-牛牛的01限定串
这题我想歪了,难点在于如何计算相似后缀的权值和?我用区间dp去写了
根据官方题解的方法是,后缀转换为前缀,不过转换为矩阵方式也是很妙的方法。
dp[i][j]代表t串第i+j个字符时i个0 j 个1的方案数
#include
using namespace std;
typedef long long ll;
const ll INF=1ll<<60;
const int N=1005;
ll dp0[N][N],dp1[N][N],valpre,valsuf,a[N][N];
int n,x,y,cnt0,cnt1;
char s[N],t[N];
bool ck(int x,int y)
{
if(x>cnt0||x<0||y>cnt1||y<0)return false;
return true;
}
int main()
{
scanf("%d %d %d %lld %lld",&n,&cnt0,&cnt1,&valpre,&valsuf);
scanf("%s %s",s,t);
for(int i=0;i<=n;++i)
{
for(int j=0;j<=n;++j)
{
dp0[i][j]=INF;
dp1[i][j]=-INF;
}
}
x=y=0;
for(int i=0;i
另一种dp:dp[i][j]代表t串第i个字符 有j个0 串 (或j个1串的最大权值和)
#include
#define inf 0x3f3f3f3f
using namespace std;
const int N=1005;
typedef long long ll;
int n,c0,c1,mn[N][N],mx[N][N],pre,bf,p0[N],p1[N],b0[N],b1[N];
char s[N],t[N];
int main()
{
scanf("%d%d%d%d%d",&n,&c0,&c1,&pre,&bf);
scanf("%s",s+1);
scanf("%s",t+1);
for(int i=1;i<=n;i++)
{
p0[i]=p0[i-1]+(s[i]=='0');
p1[i]=p1[i-1]+(s[i]=='1');
}
for(int i=n;i>=1;i--)
{
b0[i]=b0[i+1]+(s[i]=='0');
b1[i]=b1[i+1]+(s[i]=='1');
}
memset(mn,inf,sizeof(mn));
memset(mx,-inf,sizeof(mx));
mn[0][0]=mx[0][0]=0;
for(int i=0;i
E-牛牛的斐波那契字符串
这是个好题啊,来看下官方题解:
意思就是当f[n]字符串足够长的时候就可以用上面的转移方程,字符串连接只有两种情况 c1、c2。但是转移方程是分 奇偶的 怎么写这类矩阵快速幂呢?
构造矩阵如下
就可以完美解决奇偶问题啦。由于每次求了两个dp[i] 所以 快速幂的时候指数n要除2
具体看代码了。
#include
using namespace std; typedef long long ll; const ll mod=1e9+7; const int MAXN=3; int i,i0,n,nex[100005],ne[100005]; string pre[55],suf[55],s,str[55]; struct Matrix { ll mat[MAXN][MAXN]; Matrix() {} Matrix operator*(Matrix const &b)const { Matrix res; memset(res.mat, 0, sizeof(res.mat)); for (int i = 0 ;i < MAXN; i++) for (int j = 0; j < MAXN; j++) for (int k = 0; k < MAXN; k++) res.mat[i][j] = (res.mat[i][j]+this->mat[i][k] * b.mat[k][j]%mod)%mod; return res; } }; Matrix pow_mod(Matrix base, ll n) { Matrix res; memset(res.mat, 0, sizeof(res.mat)); for (int i = 0; i < MAXN; i++) res.mat[i][i] = 1; while (n > 0) { if (n & 1) res = res*base; base = base*base; //printf("res.mat:%lld\n",res.mat[0][0]); n >>= 1; } return res; } struct MAT { int mat[MAXN][MAXN]; MAT operator*(const MAT &a)const { MAT b; memset(b.mat,0,sizeof(b.mat)); for(int i=0;i >=1; } return r; } void get(string b) //常规处理方法 { ne[0]=-1; for(int i=0,j=-1;i >n; cin>>str[1]>>str[2]>>s; int d=0; for(int i=3;!d&&i<=n;i++) { if(i!=3&&str[i-1].length()>=s.length()&&str[i-2].length()>=s.length())d=i; else str[i]=str[i-2]+str[i-1]; } if(!d)cout<
F-牛牛的树形棋
官方的dsu做法太复杂了,代码复杂,这里安利一个 树上主席树的做法(可能我擅长主席树)
不过他的nim博弈分析不错:搬来
子树的maxdeep就是子树每个节点的maxdeep值,xor_sum就是每个节点的maxdeep的异或和,max_deep[x]就是x这个子树的max_deep
这里了解一下nim博弈:
n堆石子,每次从一堆中至少选1个石子,无法行动时则输了。
结论:n堆石子异或和为0则先手必败,否则先手必胜
至于怎么先手必胜 比如:3堆石子:1 2 4 异或和sum=7 那么对4这个节点 不考虑4的异或和:7^4=3 那么我只要将4这堆石子变成3 那么剩余的石子的异或和为0,达到了必败态。
了解了nim博弈后来看这句话:
就懂大概做法了。
怎么树上主席树呢?跑一个dfs序或者时间戳 按照dfs序建主席树即可。并不是严格的树上主席树,而是将树变成一维了。
#include
using namespace std;
const int N=5e5+10;
typedef long long ll;
int root[N],ls[40*N],rs[40*N],sum[40*N];
int n,m,mx[N],dfn[N],in[N],out[N],cnt,sz;
long long res;
vectorG[N];
void dfs(int u,int fa)
{
dfn[++sz]=u;//dfs序
in[u]=sz;
for(int v:G[u]){
if(v==fa) continue;
dfs(v,u);
mx[u]=max(mx[u],mx[v]+1);
}
out[u]=sz;
}
void up(int pre,int &o,int l,int r,int pos)
{
o=++cnt;
ls[o]=ls[pre];sum[o]=sum[pre]+1;rs[o]=rs[pre];
if(l==r) return ;
int mid=l+r>>1;
if(pos<=mid) up(ls[pre],ls[o],l,mid,pos);
else up(rs[pre],rs[o],mid+1,r,pos);
}
int qu(int pre,int o,int l,int r,int pos)
{
if(l==r) return sum[o]-sum[pre];
int mid=l+r>>1;
if(pos<=mid) return qu(ls[pre],ls[o],l,mid,pos);
return qu(rs[pre],rs[o],mid+1,r,pos);
}
int main()
{
scanf("%d",&n);
for(int i=1;i=mx[i])continue;
ans+=qu(root[in[i]-1],root[out[i]],0,n,sum^mx[i]);
}
printf("%lld\n",ans);
}
return 0;
}