T1:CF643D
解法:是一道模拟题。。。
我们可以用一个set维护全局最大值,然后再用n个set维护所有当前点的ans,用这n个set更新全局set即可完成3操作
考虑修改的影响,会影响到它和其原来父亲,并且父亲的E会改变从而导致父亲的父亲的答案也改变,新的父亲也是一样的,更新全局答案时注意处理三个点的环的情况,然后就是模拟了
Code:
#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define ll long long
#define fi first
#define se second
#define db double
#define St set<int>::iterator
#define St1 set<ll>::iterator
#define Vt1 vector<ll>::iterator
using namespace std;
inline ll read(){
ll res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
const int N=1e5+5;
int fa[N];ll ans[N],t[N];
multiset<ll>S;
struct cmp{
inline bool operator () (const int &a,const int &b){
if(ans[a]!=ans[b]) return ans[a]<ans[b];
return a<b;
}
};
struct info{
set<int,cmp>s;
int cnt;
inline vector<ll> get(){
vector<ll>ret;
if(!s.empty()) ret.push_back(ans[*s.begin()]);
if(s.size()>=2){
St it=s.end();it--;
ret.push_back(ans[*it]);
}
return ret;
}
}E[N];
inline ll son(int x){return t[x]/(2+E[x].cnt);}
inline ll own(int x){return t[x]-(1+E[x].cnt)*son(x);}
int n,q;
inline void modify(){
int a=read(),b=read();set<int>wk;
wk.insert(a);wk.insert(fa[a]);wk.insert(fa[fa[a]]);wk.insert(b);wk.insert(fa[b]);
set<int>wk1=wk;
for(St it=wk.begin();it!=wk.end();it++) wk1.insert(fa[*it]);
for(St x=wk1.begin();x!=wk1.end();x++){
vector<ll>V=E[*x].get();
for(Vt1 v=V.begin();v!=V.end();v++){
St1 it=S.find(*v+son(*x));
if(it!=S.end()) S.erase(it);
}
}
for(St it=wk.begin();it!=wk.end();it++) E[fa[*it]].s.erase(*it);
for(int st=-1;st<=1;st+=2){
for(St x=wk.begin();x!=wk.end();x++){
ans[*x]+=st*own(*x);
for(St y=wk.begin();y!=wk.end();y++)
if(fa[*y]==*x) ans[*x]+=st*son(*y);
}
if(st==-1) E[fa[a]].cnt--,fa[a]=b,E[b].cnt++;
}
for(St it=wk.begin();it!=wk.end();it++) E[fa[*it]].s.insert(*it);
for(St x=wk1.begin();x!=wk1.end();x++){
vector<ll> V=E[*x].get();
for(Vt1 v=V.begin();v!=V.end();v++) S.insert(*v+son(*x));
}
}
inline void file(){freopen("forest.in","r",stdin);freopen("forest.out","w",stdout);}
int main(){
n=read(),q=read();
for(int i=1;i<=n;i++) t[i]=read();
for(int i=1;i<=n;i++){
fa[i]=read();
E[fa[i]].cnt++,E[fa[i]].s.insert(i);
}
for(int i=1;i<=n;i++){
E[fa[i]].s.erase(i);
ans[i]=own(i);
for(St it=E[i].s.begin();it!=E[i].s.end();it++) ans[i]+=son(*it);
E[fa[i]].s.insert(i);
}
for(int i=1;i<=n;i++){
vector<ll>V=E[i].get();
for(Vt1 v=V.begin();v!=V.end();v++) S.insert(*v+son(i));
}
while(q--){
int op=read();
if(op==1) modify();
else if(op==2){int a=read();cout<<ans[a]+son(fa[a])<<"\n";}
else{
cout<<*S.begin()<<" ";
St1 it=S.end();it--;
cout<<*it<<"\n";
}
}
return 0;
}
T2:TopCoder SRM 671 BearDestroys
很容易发现是轮廓线dp,但是裸的轮廓线不能过
考虑只压一维,我们发现会对一个点造成影响的只有其左方,上方,右上三个格子,那我们就可以只压对角线,复杂度就变成了
2
n
∗
(
n
+
m
−
1
)
2^n*(n+m-1)
2n∗(n+m−1)
Code:
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
int mod;
inline int add(int x,int y){x+=y;if(x>=mod) x-=mod;return x;}
inline int dec(int x,int y){x-=y;if(x<0) x+=mod;return x;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline void inc(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void Dec(int &x,int y){x-=y;if(x<0) x+=mod;}
inline void Mul(int &x,int y){x=1ll*x*y%mod;}
const int N=55,S=(1<<15)|1;
int f[2][S],g[2][S];
int n,m,all;
int u[N],d[N];
inline int pt(int s,int d){return (s>>d&1);}
inline int re(int s,int d,int f){return pt(s,d)==f?s:(s^(1<<d));}
int main(){
n=read(),m=read(),mod=read();
all=1<<(n+1);
for(int i=0;i<m;i++) u[i]=0;
for(int i=m;i<n+m-1;i++) u[i]=i-m+1;
for(int i=0;i<n-1;i++) d[i]=i;
for(int i=n-1;i<n+m-1;i++) d[i]=n-1;
int now=0;
g[now][0]=1;f[now][0]=0;
for(int i=0;i<n+m-1;i++){
int to=now^1,nw=now;
memset(f[to],0,sizeof(f[to]));
memset(g[to],0,sizeof(g[to]));
for(int s=0;s<all;s++) if(g[nw][s] || f[nw][s]){
int fl=pt(s,n);
inc(g[to][re(s,n,0)<<1],g[nw][s]);
inc(f[to][re(s,n,0)<<1],f[nw][s]);
if(fl){
inc(g[to][re(s,n,0)<<1],g[nw][s]);
inc(f[to][re(s,n,0)<<1],f[nw][s]);
}
}
now^=1;
for(int j=0;j<n;j++){
int to=now^1,nw=now;
memset(f[to],0,sizeof(f[to]));
memset(g[to],0,sizeof(g[to]));
for(int s=0;s<all;s++) if(g[nw][s] || f[nw][s]){
int nf=f[nw][s],ng=g[nw][s];
if(j<u[i] || j>d[i]){inc(f[to][re(s,j,0)],nf);inc(g[to][re(s,j,0)],ng);continue;}
int f1=pt(s,j-1),f2=pt(s,j),f3=pt(s,j+1);
if(!f2){
if(f3){
int fl=re(s,j,0);
fl=re(fl,j+1,0);
inc(f[to][fl],add(add(nf,nf),add(ng,ng)));
inc(g[to][fl],add(ng,ng));
if(j==n-1){
fl=re(s,j,0);fl=re(fl,j+1,0);
inc(f[to][fl],add(add(nf,nf),add(ng,ng)));
inc(g[to][fl],add(ng,ng));
}
else{
int fl=re(s,j,1);
inc(f[to][fl],nf),inc(g[to][fl],ng);
}
}
else{
int fl=re(s,j,1);
inc(f[to][fl],nf),inc(g[to][fl],ng);
}
}
else{
int fl=re(s,j,0);inc(nf,nf),inc(ng,ng);
if(!f1){
inc(f[to][fl],add(add(nf,nf),add(ng,ng)));
inc(g[to][fl],add(ng,ng));
}
else{
inc(f[to][fl],add(nf,ng));
inc(g[to][fl],ng);
}
}
}
now^=1;
}
}
int ans=0;
inc(ans,f[now][0]);
inc(ans,mul(2,f[now][1<<n-1]));
inc(ans,mul(2,f[now][1<<n]));
inc(ans,mul(4,f[now][3<<n-1]));
cout<<ans;
return 0;
}
T3:
你有n种果汁,每种果汁有一个体积,你要找到尽量少的容量无限的桶,把所有果汁放进桶中,满足以下条件:
1.一个桶最多放两种果汁
2.任意两个桶放的果汁的体积相同
果汁的体积可以是实数
第一个引理:n种果汁最多需要n-1个桶
证明:考虑数学归纳法,
n
=
2
n=2
n=2显然成立
n
>
2
n\gt2
n>2时,考虑最少的和最多的,它们加起来一定大于等于平均值*2,那就把全部最小的和一部分最大的放进去,就变成了n-1的情况
第二个引理:如果有把a种果汁放进b个桶的方案,则一定可以放进a-b个桶,每个桶的果汁平均数不变
证明:考虑把有某种果汁的所有桶连边,则连通块个数至少为a-b
考虑形成连通块的某x个桶,有2x个空档,连通块内部的每一条边都会使不同的果汁数-1,那么最多只有2x-(x-1)=x+1种。
如果某个连通块里有超过x-1条边,那么总的连通块个数显然会增加,直接把这个连通块并到别的连通块里去就行了。
则我们就是要把所有数分成尽量多的段,使各段平均数相等
先枚举答案,再搜索枚举集合就完了
Code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
int n,all,a[25];ll suma;
int sum[2333333],cnt[2333333],num[2333333];
inline bool check(int x){
int s=0;
while(1){
int t=all^s,fl=0;
if(1ll*(cnt[t]-1)*suma==1ll*sum[t]*x) return 1;
for(int tt=t;tt;tt=(tt-1)&t)
if(1ll*(cnt[tt]-1)*suma==1ll*sum[tt]*x){fl=1;s^=tt;break;}
if(!fl) return 0;
}
}
int main(){
n=read();all=(1<<n)-1;
if(n<=2){puts("1");return 0;}
for(int i=1,k=1;i<=n;i++,k<<=1) a[i]=read(),suma+=a[i],num[k]=a[i];
for(int s=1;s<=all;s++) sum[s]=sum[s^(s&-s)]+num[s&-s],cnt[s]=cnt[s^(s&-s)]+1;
for(int i=(n+1)/2;i<n;i++) if(check(i)){cout<<i;return 0;}
}