道路
简单的难以置信的树上dp,一度以为自己看错题了
#include<bits/stdc++.h>
#define N 20010
using namespace std;
int a[N],b[N],c[N];
int gl[N],tl[N];
long long dp[N][42][42];
int n,tot;
long long dfs(int p,int x,int y){
if(p<0){
p=-p;
return 1ll*c[p]*(a[p]+x)*(b[p]+y);
}
if(dp[p][x][y]!=0) return dp[p][x][y];
return dp[p][x][y]=min(dfs(gl[p],x+1,y)+dfs(tl[p],x,y),dfs(gl[p],x,y)+dfs(tl[p],x,y+1));
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++) scanf("%d%d",&gl[i],&tl[i]);
for(int i=1;i<=n;i++) scanf("%d%d%d",&a[i],&b[i],&c[i]);
printf("%lld\n",dfs(1,0,0));
return 0;
}
排列
神奇贪心,不难发现限制条件可以转化为在按顺序有根森林中选点,每次只能选某个根节点,然后把这个点删掉。
这样还不够,我们考虑每次选择:如果最小的权值是个根节点,肯定直接取走,否则在取走这个点的父亲节点后,下一次一定直接选它,这样的话我们可以每次将最小节点与它的父亲并到一起,新的权值为这个块里的权值平均值(显然的贪心???)。这里可以用并查集和堆维护一下。
#include<bits/stdc++.h>
#define N 500100
using namespace std;
inline int read(){
int a=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch<='9'&&ch>='0') a=a*10+ch-'0',ch=getchar();
return a;
}
struct Node{
long long zon;
int siz,zg;
};
bool operator <(Node a,Node b){return a.zon*b.siz>b.zon*a.siz;}
priority_queue<Node> q;
int n,m;
int fa[N];
bool done[N];
long long w[N];
int a[N],s[N];
int find_fa(int a){
if(fa[a]==a) return a;
return fa[a]=find_fa(fa[a]);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
for(int i=1;i<=n;i++) s[i]=1,q.push((Node){w[i],1,i});
long long ans=0;
int tot=0;
while(!q.empty()){
Node st=q.top();q.pop();
if(done[st.zg]) continue;
if(s[st.zg]==st.siz) done[st.zg]=1;
else continue;
if(find_fa(a[st.zg])==0){
ans+=st.zon*(tot+1);
tot+=st.siz;
fa[st.zg]=0;
}
else{
int l=find_fa(a[st.zg]),r=st.zg;
fa[r]=l;
ans+=w[r]*s[l];
s[l]+=s[r];
w[l]+=w[r];
q.push((Node){w[l],s[l],l});
}
}
if(tot!=n) puts("-1");
else printf("%lld\n",ans);
return 0;
}
游戏
以下是最慢点3s,BZOJ AC现场90分的N^2暴力
#include<bits/stdc++.h>
#define N 1001000
using namespace std;
inline void read(int &a){
a=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch<='9'&&ch>='0') a=a*10+ch-'0',ch=getchar();
}
int n,m,q;
int ned[N];
int l[N],r[N];
int main(){
read(n);read(m);read(q);
ned[n]=-1,ned[0]=-1;
for(int i=0,a,b;i<m;i++){
read(a);read(b);
ned[a]=b;
}
for(int i=n;i>=1;i--){
l[i]=i,r[i]=i;
int f=1;
while(f){
f=0;
if(ned[l[i]-1]<=r[i]&&ned[l[i]-1]>=l[i]||ned[l[i]-1]==0){
l[i]--,f=1;
}
if(ned[r[i]]<=r[i]&&ned[r[i]]>=l[i]||ned[r[i]]==0){
r[i]++,f=1;
l[i]=min(l[i],l[r[i]]);
r[i]=max(r[i],r[r[i]]);
}
}
}
for(int i=1,s,t;i<=q;i++){
scanf("%d%d",&s,&t);
if(t<=r[s]&&t>=l[s]) puts("YES");
else puts("NO");
}
return 0;
}
寻宝游戏
本机卡常AC,提交疯狂TLE,复杂度O(NMQ)。。。
#include<bits/stdc++.h>
#define M 5010
#define N 1024
using namespace std;
char buf[30000000],*cp=buf;
inline void read(int &a){
a=0;
while(*cp<'0'||*cp>'9') *cp++;
while(*cp<='9'&&*cp>='0') a=a*10+(*cp++ & 15);
}
inline void rd(char *s){
while(!isdigit(*cp)) cp++;
while(isdigit(*cp)) *s++ = *cp++;
*s=0;
}
const int MOD=1e9+7;
int n,m,q,ans;
char s[M];
int a[N][M],c[M];
int cop[N][M];
int cf[N];
void dfs(int p,int res){
if(!res){
ans=ans+cf[p];
if(ans>=MOD) ans-=MOD;
return;
}
if(p==0){
for(register int i=0;i!=res;++i) if(c[cop[p][i]]) return;
ans++;
if(ans>=MOD) ans-=MOD;
return;
}
int fa=1,fo=1;
for(register int i=0;i!=(res/4)*4;i+=4){
if(a[p][cop[p][i]]==0&&c[cop[p][i]]){
fa=0;
break;
}
if(a[p][cop[p][i+1]]==0&&c[cop[p][i+1]]){
fa=0;
break;
}
if(a[p][cop[p][i+2]]==0&&c[cop[p][i+2]]){
fa=0;
break;
}
if(a[p][cop[p][i+3]]==0&&c[cop[p][i+3]]){
fa=0;
break;
}
}
for(register int i=(res/4)*4;i!=res;i++)
if(a[p][cop[p][i]]==0&&c[cop[p][i]]){
fa=0;
break;
}
for(register int i=0;i!=(res/4)*4;i+=4){
if(a[p][cop[p][i]]&&c[cop[p][i]]==0){
fo=0;
break;
}
if(a[p][cop[p][i+1]]&&c[cop[p][i+1]]==0){
fo=0;
break;
}
if(a[p][cop[p][i+2]]&&c[cop[p][i+2]]==0){
fo=0;
break;
}
if(a[p][cop[p][i+3]]&&c[cop[p][i+3]]==0){
fo=0;
break;
}
}
for(register int i=(res/4)*4;i!=res;i++)
if(a[p][cop[p][i]]&&c[cop[p][i]]==0){
fo=0;
break;
}
int nres;
if(fa){
nres=0;
for(register int i=0;i!=(res/4)*4;i+=4){
if(a[p][cop[p][i]]) cop[p-1][nres++]=cop[p][i];
if(a[p][cop[p][i+1]]) cop[p-1][nres++]=cop[p][i+1];
if(a[p][cop[p][i+2]]) cop[p-1][nres++]=cop[p][i+2];
if(a[p][cop[p][i+3]]) cop[p-1][nres++]=cop[p][i+3];
}
for(register int i=(res/4)*4;i<res;i++){
if(a[p][cop[p][i]]) cop[p-1][nres++]=cop[p][i];
}
dfs(p-1,nres);
}
if(fo){
nres=0;
for(register int i=0;i!=(res/4)*4;i+=4){
if(!a[p][cop[p][i]]) cop[p-1][nres++]=cop[p][i];
if(!a[p][cop[p][i+1]]) cop[p-1][nres++]=cop[p][i+1];
if(!a[p][cop[p][i+2]]) cop[p-1][nres++]=cop[p][i+2];
if(!a[p][cop[p][i+3]]) cop[p-1][nres++]=cop[p][i+3];
}
for(register int i=(res/4)*4;i<res;i++){
if(!a[p][cop[p][i]]) cop[p-1][nres++]=cop[p][i];
}
dfs(p-1,nres);
}
}
int main(){
buf[fread(buf,1,30000000,stdin)] = 0;
read(n);read(m);read(q);
cf[0]=1;
for(int i=1;i<=n+1;i++) cf[i]=cf[i-1]*2%MOD;
for(int i=1;i<=n;i++){
rd(s);
for(int j=0;j<m;j++) a[i][j]=s[j]-'0';
}
while(q--){
ans=0;
rd(s);
for(int i=0;i<m;i++) c[i]=s[i]-'0';
for(int i=0;i<m;i++) cop[n][i]=i;
dfs(n,m);
printf("%d\n",ans);
}
return 0;
}
毒瘤
考虑建出一个生成树,那么会余下一些非树边(返祖边),最多11条。
考虑枚举这些边的选择情况,一共有三种:选一端,选另一端,都不选。这样就可以DP了,复杂度n*3^11,期望得分60-70。
事实上,我们只用考虑两种情况:选深度小的一端,不选深度小的一端,在DP的把深度大的一端也DP进去就行了,复杂度n*2^11,期望得分70-80分。
这还过不去。。。发现每个节点并不可能包含所有的端点,也就是说这东西其实跑不满,或许可以弄个动态的数组乱搞一下?但是我实在不想写了。。。