T1 一雙木棋chess
n,m<=10
Alice想最大化差值,Bob想最小化差值。發現棋子呈階梯狀排布,總狀態數階梯狀排布,階乘複雜度。
哈希記錄狀態後爆搜,記錄當前是誰的回合,按照自身的決策去最大化/最小化價值。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int base=15;
const ll INF=2e18;
map<ll,int>ma;
int st[15],A[15][15],B[15][15],n,m;
ll range;
ll gethash(){
ll ans=0;
for(int i=1;i<=n;i++)ans=ans*base+st[i];
return ans;
}
ll dfs(ll S,int ty){
if(ma[S])return ma[S];
if(S==range)return 0;
ll ans=ty==0?-INF:INF;
for(int i=1;i<=n;i++){
if(st[i]<st[i-1]){
st[i]++;
if(ty==0)ans=max(ans,dfs(gethash(),ty^1)+A[i][st[i]]);
else ans=min(ans,dfs(gethash(),ty^1)-B[i][st[i]]);
st[i]--;
}
}
return ma[S]=ans;
}
int main(){
// freopen("chess.in","r",stdin);
// freopen("chess.out","w",stdout);
memset(st,0,sizeof(st));
scanf("%d%d",&n,&m);st[0]=m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&A[i][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&B[i][j]);
}
}
range=0;
for(int i=1;i<=n;i++)range=range*base+m;
cout<<dfs(0,0);
}
T2.IIIDX
讀題題意即 有一些形態相近的樹組成的森林,樹的形態給定,每個節點從給定權值中賦予一個權值,使得每棵樹都是一個小根堆,並且要求字典序最大的方案。
把每棵樹根連向0號節點形成一棵樹。很直接的思路,排序後,對於同層次的樹,右邊的填小的留出位置給左邊。左邊填可控區間內最小的。
但是題目中有一個很奇怪的條件就是,數值可能會相同。。遇到奇怪的條件想一下反例。發現這樣做在有相同時是合法的而不是最優的。
我們考慮一個排好序的序列,從中找出一個點填入,使這個點的值儘量大。提前就要預留好他子樹中的位置,在有位置的情況下,找最左邊的(從大到小排序)。然後他右邊的節點都少了這些點的位置。對於一堆權值相同的節點,爲了保證正確性,我們取這裏面最右邊的節點。
這個操作是可以用線段樹實現的。
對於他父親已經預留了位置,在查詢第一個孩子時要把這個操作還原回去。
線段樹葉子記錄每個節點左邊包括自身還剩幾個空節點。這個具有單調性。
取一個min在線段樹上二分就可以得到答案、
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+5;
struct edge{
int to,next;
}e[MAXN<<1];
int head[MAXN],cnt=0;
inline void add(int u,int v){e[++cnt]=(edge){v,head[u]},head[u]=cnt;}
inline int read(){
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
bool cmp(int a,int b){
return a>b;
}
int size[MAXN],d[MAXN],after[MAXN],fa[MAXN],output[MAXN],n;
double kk;
void dfs(int u){
size[u]=1;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
dfs(v);
size[u]+=size[v];
}
}
struct xds{
#define lson (o<<1)
#define rson (o<<1|1)
int minv[MAXN<<2],lzt[MAXN<<2];
inline void pushup(int o){
minv[o]=min(minv[lson],minv[rson]);
}
inline void pushdown(int o){
if(lzt[o]){
lzt[lson]+=lzt[o];lzt[rson]+=lzt[o];
minv[lson]+=lzt[o];minv[rson]+=lzt[o];
lzt[o]=0;
}
}
inline void build(int o,int l,int r){
if(l==r){minv[o]=l;return;}
int mid=l+r>>1;
build(lson,l,mid);build(rson,mid+1,r);
pushup(o);
}
inline void change(int o,int l,int r,int ql,int qr,int val){
if(ql<=l&&qr>=r){minv[o]+=val;lzt[o]+=val;return;}
int mid=l+r>>1;
pushdown(o);
if(ql<=mid)change(lson,l,mid,ql,qr,val);
if(qr>=mid)change(rson,mid+1,r,ql,qr,val);
pushup(o);
}
inline int find(int o,int l,int r,int k){
if(l==r){return minv[o]>=k?l:l+1;}
pushdown(o);
int mid=l+r>>1;
if(k>minv[rson])return find(rson,mid+1,r,k);
return find(lson,l,mid,k);
}
#undef lson
#undef rson
}T;
int main(){
// freopen("iiidx.in","r",stdin);
// freopen("iiidx.out","w",stdout);
scanf("%d%lf",&n,&kk);
for(int i=1;i<=n;i++){
d[i]=read();
}
for(int i=n;i>=1;i--){
int p=int(i/kk);
fa[i]=p;
add(p,i);
}
sort(d+1,d+1+n,cmp);
for(int i=n;i>=1;i--)if(d[i]==d[i+1])after[i]=after[i+1]+1; else after[i]=0;
T.build(1,1,n);
dfs(0);
// for(int i=1;i<=n;i++)cout<<fa[i]<<" "<<size[i]<<" "<<after[i]<<endl;
for(int i=1;i<=n;i++){
if(fa[i]&&fa[i]!=fa[i-1])T.change(1,1,n,output[fa[i]],n,size[fa[i]]-1);
int p=T.find(1,1,n,size[i]);
p+=after[p];after[p]++;p-=after[p]-1;
output[i]=p;
T.change(1,1,n,p,n,-size[i]);
}
for(int i=1;i<=n;i++)printf("%d ",d[output[i]]);
puts("");
return 0;
}
T3 祕密襲擊coat
標算不會,有一種優美的暴力。
題意爲 給一棵樹,從中選出所有不相同的元素個數大於k的連通塊,求這些連通塊中第k大元素權值和。
答案對64123取模。
我們考慮分別計算每個點的答案。
如果這個點在塊中爲第k大的連通塊有v個。對答案的貢獻爲vs。
問題轉化爲求出每個點是第k大元素的連通塊個數。
枚舉每個點作爲根。
如果這個點rank<k 可以直接剪掉。
否則統計答案。
因爲連通塊的信息是跨子樹的,所以我們不能像通常一下子樹上傳。還要從父節點下傳信息,再子樹更新信息。
如果下傳時他碰到了一個權值比他大的節點v,v與root所在連通塊中,root爲第k大的方案數等於 root與v的father連通塊中,root爲第k-1大的方案數。若v權值比root小則爲k大的方案數。
子節點信息上傳更新父節點計算答案即可。這樣在點的權值均不相同時顯然是對的。
若點的權值相同,我們可以強行按標號順序區分權值大小,其餘做法同上。
複雜度分析:
只有 個點 每個點作爲根進行 。每次dfs遍歷 個點,每個點上傳加下傳複雜度
整體複雜度 二次函數當 取得 時複雜度最差爲
因爲良心出題人並沒有卡,所以可以A。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=2000;
const int MOD=64123;
ll f[MAXN][MAXN];
struct edge{
int to,next;
}e[MAXN<<1];
int head[MAXN],val[MAXN],cnt=0,V,n,m,k,w;
inline void add(int u,int v){
e[++cnt]=(edge){v,head[u]},head[u]=cnt;
e[++cnt]=(edge){u,head[v]},head[v]=cnt;
}
inline ll ADD(ll x,ll y){
x+=y;if(x>=MOD)x-=MOD;
return x;
}
void dfs(int u,int fa){
if(val[u]>val[V]||(val[u]==val[V]&&u>V))for(int i=1;i<=k;i++)f[u][i]=f[fa][i-1];
else for(int i=1;i<=k;i++)f[u][i]=f[fa][i];
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v==fa)continue;
dfs(v,u);
for(int j=1;j<=n;j++)f[u][j]=ADD(f[u][j],f[v][j]);
}
}
ll ans=0;
void calc(int p){
//memset(f,0,sizeof())
int tim=1;
for(int i=1;i<=n;i++)if(val[i]>val[p]||(val[i]==val[p]&&i>p))tim++;
if(tim<k)return;
V=p;
for(int i=0;i<=n;i++)f[p][i]=0;
f[p][1]=1;
for(int i=head[p];i;i=e[i].next){
dfs(e[i].to,p);
for(int j=1;j<=n;j++)f[p][j]=ADD(f[p][j],f[e[i].to][j]);
}
//cout<<f[p][3]<<" "<<p<<endl;
ans+=val[p]*f[p][k];
}
int main(){
//freopen("coat.in","r",stdin);
//freopen("coat.out","w",stdout);
scanf("%d%d%d",&n,&k,&w);
for(int i=1;i<=n;i++){
scanf("%d",&val[i]);
}
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
}
for(int i=1;i<=n;i++){
calc(i);
}
cout<<ans%MOD<<endl;
return 0;
}