C-牛牛的揠苗助長
二分天數然後三分高度check即可。
#include<bits/stdc++.h>
#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<int, int>
#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<r)
{
ll m1=l+r>>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<<ans<<endl;
}
int main()
{
solve();
}
/*
6
2 2 2 2 2 3
*/
D-牛牛的01限定串
這題我想歪了,難點在於如何計算相似後綴的權值和?我用區間dp去寫了
根據官方題解的方法是,後綴轉換爲前綴,不過轉換爲矩陣方式也是很妙的方法。
dp[i][j]代表t串第i+j個字符時i個0 j 個1的方案數
#include<bits/stdc++.h>
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<n;++i)
{
if(s[i]=='0')++x;
else ++y;
if(ck(x,y))a[x][y]+=valpre;
}
x=cnt0,y=cnt1;
for(int i=n-1;~i;--i)
{
if(s[i]=='0')--x;
else --y;
if(ck(x,y))a[x][y]+=valsuf;
}
dp0[cnt0][cnt1]=dp1[cnt0][cnt1]=a[cnt0][cnt1];
for(int i=cnt0;~i;--i)//從右下角往左上角走
{
for(int j=cnt1;~j;--j)
{
if(t[i+j]=='0')
{
dp0[i][j]=min(dp0[i][j],dp0[i+1][j]+a[i][j]);
dp1[i][j]=max(dp1[i][j],dp1[i+1][j]+a[i][j]);
}
if(t[i+j]=='1')
{
dp0[i][j]=min(dp0[i][j],dp0[i][j+1]+a[i][j]);
dp1[i][j]=max(dp1[i][j],dp1[i][j+1]+a[i][j]);
}
if(t[i+j]=='?')
{
dp0[i][j]=min(dp0[i][j],dp0[i+1][j]+a[i][j]);
dp1[i][j]=max(dp1[i][j],dp1[i+1][j]+a[i][j]);
dp0[i][j]=min(dp0[i][j],dp0[i][j+1]+a[i][j]);
dp1[i][j]=max(dp1[i][j],dp1[i][j+1]+a[i][j]);
}
}
}
printf("%lld %lld\n",dp0[0][0],dp1[0][0]);
return 0;
}
另一種dp:dp[i][j]代表t串第i個字符 有j個0 串 (或j個1串的最大權值和)
#include<bits/stdc++.h>
#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<n;i++)
for(int j=0;j<=min(i,c0);j++)
{
if(c0-j==b0[i+1]&&c1-(i-j)==b1[i+1])
{
mn[i][j]+=bf;
mx[i][j]+=bf;
}
if(j<c0&&(t[i+1]=='?'||t[i+1]=='0'))
{
if(j+1==p0[i+1]&&i-j==p1[i+1])
{
mn[i+1][j+1]=min(mn[i+1][j+1],mn[i][j]+pre);
mx[i+1][j+1]=max(mx[i+1][j+1],mx[i][j]+pre);
}
else
{
mn[i+1][j+1]=min(mn[i+1][j+1],mn[i][j]);
mx[i+1][j+1]=max(mx[i+1][j+1],mx[i][j]);
}
}
if(i-j<c1&&(t[i+1]=='?'||t[i+1]=='1'))
{
if(j==p0[i+1]&&i-j+1==p1[i+1])
{
mn[i+1][j]=min(mn[i+1][j],mn[i][j]+pre);
mx[i+1][j]=max(mx[i+1][j],mx[i][j]+pre);
}
else
{
mn[i+1][j]=min(mn[i+1][j],mn[i][j]);
mx[i+1][j]=max(mx[i+1][j],mx[i][j]);
}
}
}
printf("%d %d\n",mn[n][c0],mx[n][c0]);
}
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<bits/stdc++.h>
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;
vector<int>G[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<n;++i){
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,-1);
int sum=0;
for(int i=1;i<=n;++i) sum^=mx[i];
ll ans=0;
if(!sum)puts("NO");
else{
puts("YES");
for(int i=1;i<=n;i++) up(root[i-1],root[i],0,n,mx[dfn[i]]);
for(int i=1;i<=n;++i){
if((sum^mx[i])>=mx[i])continue;
ans+=qu(root[in[i]-1],root[out[i]],0,n,sum^mx[i]);
}
printf("%lld\n",ans);
}
return 0;
}