(由于先看的最后一题,然后又一直WA,导致这场有点爆炸,我背锅。

A .Fancy Antiques

题意: 选择最多k个商店,买n个物品,每个物品分别对应两个店售卖,求最小花费是多少。n<100,k=m<=40;

思路:搜索。。。。开始以为是个费用流,然后没法限制。加N多减枝,然后....

 

B. 好像没什么可以学的,懒得看了

 

C .Greetings!

pro:N种卡片,用K种格子去装。问浪费的空间。N,K<15

sol:比较小,状态压缩。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
int n,k,w[20],h[20],q[20];
long long f[1<<16][20];
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)scanf("%d%d%d",w+i,h+i,q+i);
    memset(f,127,sizeof(f));
    for(int i=0;i<=k;i++)f[0][i]=0;
    for(int i=1;i<(1<<n);i++)
        for(int p=i;p;p=(p-1)&i){
            int maxw=0,maxh=0;long long now=0;
            for(int j=1;j<=n;j++)
                if(1<<(j-1)&p)maxw=max(maxw,w[j]),maxh=max(maxh,h[j]);
            for(int j=1;j<=n;j++)
                if(1<<(j-1)&p)now+=(long long)(maxw*maxh-w[j]*h[j])*q[j];
            for(int j=1;j<=k;j++)
                f[i][j]=min(f[i][j],f[i^p][j-1]+now);
        }
    printf("%lld",f[(1<<n)-1][k]);
    return 0;
}
View Code

 

D .Programming Team

题意:给定一棵大小为N的点权树(si,pi),现在让你选敲好K个点,需要满足如果如果u被选了,那么fa[u]一定被选,现在要求他们的平均值(pi之和/si之和)最大。

思路:均值最大,显然需要01分数规划,但是后面怎么高效的做背包,我是不会的,我只会暴力的背包,O(N*K*K)。 还好队友会,叫“树上依赖背包”,即要问树转化为DFS序,按照倒序来DP,复杂度O(N*K)。dp[i][j]=max(dp[i+1][j-v[x]]+w[x],dp[i+sz[x]][j]),分别对应x选或者不选(其中x是DFS序为i的点)。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2610;
const double inf=1e20;
double dp[maxn][maxn],s[maxn],p[maxn],val[maxn];
int r[maxn],Laxt[maxn],Next[maxn],To[maxn];
int pos[maxn],tot,cnt,N,K,sz[maxn];
void add(int u,int v)
{
    Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v;
}
void dfs(int u)
{
    sz[u]=1; tot++; pos[tot]=u;
    for(int i=Laxt[u];i;i=Next[i]){
        dfs(To[i]); sz[u]+=sz[To[i]];
    }
}
bool check(double Mid)
{
    rep(i,1,N) val[i]=p[i]-s[i]*Mid;
    rep(i,1,tot+1) rep(j,1,K) dp[i][j]=-inf;
    rep(i,0,tot+1) dp[i][0]=0;
    for(int i=tot;i>=1;i--){
        int x=pos[i];
        for(int j=1;j<=K;j++){
            dp[i][j]=max(dp[i+1][j-1]+val[x],dp[i+sz[x]][j]);
        }
    }
    return dp[1][K]>=0;
}
int main()
{
    scanf("%d%d",&K,&N); K++;
    rep(i,1,N){
        scanf("%lf%lf%d",&s[i],&p[i],&r[i]);
        add(r[i],i);
    }
    dfs(0);
    double L=0,R=10000,Mid,ans=0; int T=30;
    while(T--){
        Mid=(L+R)/2;
        if(check(Mid)) ans=max(Mid,ans),L=Mid;
        else R=Mid;
    }
    printf("%.3lf\n",ans);
    return 0;
}
View Code

 

E. K-Inversions

pro:对(A,B)的长度贡献。

sol:FFT模板题。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
struct cp{
    double x,y;
    cp operator +(const cp&a)const{
        return (cp){x+a.x,y+a.y};
    }
    cp operator -(const cp&a)const{
        return (cp){x-a.x,y-a.y};
    }
    cp operator *(const cp&a)const{
        return (cp){x*a.x-y*a.y,x*a.y+y*a.x};
    }
}a[5000000],b[5000000],c[5000000];
int n,m,len,ans[5000000];char s[1000010];
double pi=3.14159265358979323846;
void DFT(cp *a,int len,int type){
    for(int i=0,t=0;i<len;i++){
        if(i<t)std::swap(a[i],a[t]);
        for(int j=len>>1;(t^=j)<j;j>>=1);
    }
    for(int h=2;h<=len;h<<=1){
        cp wn=(cp){cos(pi*2*type/h),sin(pi*2*type/h)};
        for(int i=0;i<len;i+=h){
            cp w=(cp){1,0},t;
            for(int j=0;j<h>>1;j++,w=w*wn){
                t=a[i+j+(h>>1)]*w;
                a[i+j+(h>>1)]=a[i+j]-t;
                a[i+j]=a[i+j]+t;
            }
        }
    }
}
int main(){
    scanf("%s",s);n=strlen(s);
    for(int i=0;i<n;i++)
        if(s[i]=='A')a[i].x=1;
        else b[n-i-1].x=1;
    for(len=1;len<=n+n;len<<=1);
    DFT(a,len,1),DFT(b,len,1);
    for(int i=0;i<len;i++)c[i]=a[i]*b[i];
    DFT(c,len,-1);
    for(int i=0;i<len;i++)ans[i]=c[i].x/len+0.1;
    for(int i=n;i<n+n-1;i++)printf("%d\n",ans[i]);
    return 0;
}
View Code

 

G .Symmetry

pro:现在给你二维平面上N个点,现在让你求,加最少的点,使得所有点关于同一个点或者线有对称点。N<1e3;

sol:好像是枚举的,枚举对称中心,枚举对称轴,复杂度O(N^3);

 

H .Jewel Thief

pro:给定N,M。输入N个物品,(si,vi)表示第i个物品体积为si,价值为vi,s<=300,vi<=1e9; N<1e6;现在要求,对于背包体积为1到M时,求出最大背包价值。

sol:显然直接跑背包会爆炸。 发现物品体积都比较小,我们先对相同体积的排序,对于体积相同的一起处理。

然后发现转移都是在差为体积整数倍之间,按照容量对体积取模分组 ,发现组内部转移满足神奇的决策单调性。 然后就是s次分治。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rep2(i,a,b) for(int i=b;i>=a;i--)
using namespace std;
const int maxn=310;
const int maxm=100010;
ll dp[2][maxm];
vector<ll>G[maxn]; int x,d,t;
void get(int L,int R,int l,int r)
{
    if(L>R) return ;
    int Mid=(L+R)>>1,pos=Mid;
    for(int i=min(r,Mid-1);i>=l;i--){
        if(Mid-i>G[x].size()) break;
        if(dp[t][d+Mid*x]<dp[t^1][d+i*x]+G[x][Mid-i-1]){
            pos=i; dp[t][d+Mid*x]=dp[t^1][d+i*x]+G[x][Mid-i-1];
        }
    }
    get(L,Mid-1,l,pos);
    get(Mid+1,R,pos,r);
}
int main()
{
    int N,M,s;ll v;
    scanf("%d%d",&N,&M);
    rep(i,1,N){
        scanf("%d%lld",&s,&v);
        G[s].push_back(v);
    }
    rep(i,1,300){
        if(G[i].size()==0) continue;
        sort(G[i].begin(),G[i].end(),greater<int>() );
        for(int j=1;j<G[i].size();j++) G[i][j]+=G[i][j-1];
        t^=1;  rep(j,1,M) dp[t][j]=dp[t^1][j];
        rep(j,0,i-1){
            d=j;  x=i;
            get(0,(M-j)/i,0,(M-j)/i);
        }
        rep(j,1,M) dp[t][j]=max(dp[t][j],dp[t][j-1]);
    }
    rep(i,1,M) printf("%lld ",dp[t][i]);
    return 0;
}
View Code

 

 

I. Tourists

pro:给定一棵树,求所有倍数关系的点之间距离之和。

sol:枚举所有的,倍增求LCA,O(N*logN*logN),也可以用ST表O(NlogN);

#include<bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
struct edge{
    int d,nex;
}e[400010];
int n,q[200010],l,efree,d[200010],f[20][200010];
long long ans;
inline void add(int x,int y){e[++efree]=(edge){y,q[x]};q[x]=efree;}
void dfs(int x,int y){
    f[0][x]=y;d[x]=d[y]+1;
    for(int i=q[x];i;i=e[i].nex)
        if(e[i].d!=y)dfs(e[i].d,x);
}
inline int lca(int x,int y){
    if(d[x]>d[y])swap(x,y);
    for(int i=log2(d[y]-d[x]);i>=0;i--)
        if(d[f[i][y]]>=d[x])y=f[i][y];
    if(x==y)return x;
    for(int i=log2(d[x]);i>=0;i--)
        if(f[i][x]!=f[i][y])x=f[i][x],y=f[i][y];
    return f[0][x];
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int x,y;scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }
    dfs(1,0);l=log2(n);
    for(int i=1;i<=l;i++)
        for(int j=1;j<=n;j++)f[i][j]=f[i-1][f[i-1][j]];
    for(int i=1;i<=n>>1;i++)
        for(int j=i+i;j<=n;j+=i){
            int fa=lca(i,j);
            ans+=d[i]+d[j]-2*d[fa]+1;
        }
    printf("%lld",ans);
    return 0;
}
View Code

 

F. Mountain Scenes

pro:给定总长度为N的木棍,一个W*H的框框,问有多少种分放方式,使得图案不是平的。N<10000;W,H<100

sol:DP即可,用总的分放方案减去平的。

#include<bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
ll dp[101][10001];
const int MOD = 1e9 + 7;
int main()
{
    int n, w, h;
    scanf("%d%d%d", &n, &w, &h);
    if(n > w * h)n = w * h;
    for(int i = 0; i <= w; i++)dp[i][0] = 1;
    for(int i = 1; i <= w; i++)
        for(int j = 1; j <= n && j <= i * h; j++)
            for(int k = 0; k <= h && k <= j; k++)dp[i][j] += dp[i - 1][j - k], dp[i][j] %= MOD;
    ll ans = 0;
    for(int i = 1; i <= n; i++)ans += dp[w][i], ans %= MOD;
    ans -= (n / w);
    printf("%lld\n", ans);
    return 0;
}
View Code

 

J .Whiteboard

pro:给定N*M的画板,然后给定画板的最终状态,以及画笔的走向。现在在某个时间T,画笔变为了橡皮擦,即最开始画板的白色的,T之前笔走过是染黑,T之后笔走过的漂白,求T的范围[L,R],不存在则输出-1 -1。

sol:首先,如果画笔没有走过的地方是黑色'#',则无解。

         对于画笔走过的地方,我们关系的其实只有最后一次走过的时间ti,对于黑色,L>=ti; 对于白色,R<ti;

这种题,我们倒着去删点就可以了,删点可以利用并查集,链表,或者直接用set的二分功能即可。

注意使用long long。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=1000010;
char c[maxn],s[maxn],q[maxn][8];
set<int>RR[maxn],CC[maxn];
set<int>::iterator it;
int d[maxn],X[maxn],Y[maxn],N,M,Q;
ll times[maxn],Mx[maxn];
void solve(int nowx,int nowy,int tox,int toy,int x,int y,ll nowt)
{
    if(abs(x)==1){
        int L=min(nowx,tox),R=max(nowx,tox);
        for(it=CC[nowy].lower_bound(L);;it=CC[nowy].lower_bound(L)){
            if(it==CC[nowy].end()||(*it)>R) break;
            L=*it;CC[nowy].erase(it);
            RR[L].erase(RR[L].find(nowy));
            Mx[(L-1)*M+nowy]=nowt+abs(L-nowx);
        }
    }
    else {
        int L=min(nowy,toy),R=max(nowy,toy);
        for(it=RR[nowx].lower_bound(L);;it=RR[nowx].lower_bound(L)){
            if(it==RR[nowx].end()||(*it)>R) break;
            L=*it;RR[nowx].erase(it);
            CC[L].erase(CC[L].find(nowx));
            Mx[(nowx-1)*M+L]=nowt+abs(L-nowy);
        }
    }
}
int main()
{
    scanf("%d%d%d",&N,&M,&Q);
    for(int i=N;i>=1;i--) {
        scanf("%s",c+1);
        rep(j,1,M) s[(i-1)*M+j]=c[j];
    }
    rep(i,1,Q) scanf("%s%d",q[i],&d[i]);
    rep(i,1,N)
      rep(j,1,M) RR[i].insert(j),CC[j].insert(i);
    X[0]=1; Y[0]=1; times[0]=1;
    rep(i,1,Q){
        if(q[i][0]=='u') X[i]=X[i-1]+d[i],Y[i]=Y[i-1];
        else if(q[i][0]=='d') X[i]=X[i-1]-d[i],Y[i]=Y[i-1];
        else if(q[i][0]=='r') X[i]=X[i-1],Y[i]=Y[i-1]+d[i];
        else X[i]=X[i-1],Y[i]=Y[i-1]-d[i];
        times[i]=times[i-1]+d[i];
    }
    for(int i=Q;i>=1;i--){
        if(q[i][0]=='u') solve(X[i-1],Y[i-1],X[i],Y[i],1,0,times[i-1]);
        else if(q[i][0]=='d') solve(X[i-1],Y[i-1],X[i],Y[i],-1,0,times[i-1]);
        else if(q[i][0]=='l') solve(X[i-1],Y[i-1],X[i],Y[i],0,-1,times[i-1]);
        else solve(X[i-1],Y[i-1],X[i],Y[i],0,1,times[i-1]);
    }
    ll L=0,R=times[Q];
    rep(i,1,N*M){
        if(!Mx[i]&&s[i]=='#') R=-1;
        if(!Mx[i]) continue;
        if(s[i]=='#') L=max(L,Mx[i]);
        else R=min(R,Mx[i]-1);
    }
    if(L<=R) printf("%lld %lld\n",L,R);
    else puts("-1 -1");
    return 0;
}
View Code

 

K .YATP

题意:给定带点权边权的树,定义路径的花费=路径边权和e+起点点权w[s]*终点点权w[t]。N<2e5,e,w<1e6;

思路:首先,需要树分治。 然后得到方程dp[i]=min{ dis[i]+dis[j]+w[i]*w[j] },很显然需要斜率优化。 

      注意维护凸包的时候是需要保证w[j]是单调的,这样才能用不等式维护队尾。  由于w[i]不是对应的队尾,所以我们还要二分凸包。

     还有个问题,怎么确定我们得到的i和j不是在同一个子树呢? 因为如果在一颗子树的时候dp[i]=dis[i]+dis[j]+w[i]*w[j]-2*dis[LCA]。 其实没必要考虑这个问题,因为当LCA为根的时候会更新答案。(这一点想不到估计要很难去维护了)

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=400010;
int Laxt[maxn],Next[maxn],To[maxn],Len[maxn],cnt;
int sz[maxn],son[maxn],rt,all,vis[maxn],S[maxn],tot;
ll ans[maxn],a[maxn],dis[maxn],sum; int q[maxn],top;
bool cmp(int x,int y)
{
    int xx=x,yy=y;
    if(a[xx]==a[yy]) return dis[xx]<dis[yy];
    return a[xx]<a[yy];
}
ll getans(int p,int k)
{
    return dis[k]+dis[p]+a[p]*a[k];
}
void add(int u,int v,int w)
{
    Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; Len[cnt]=w;
}
void dfs1(int u,int f)
{
    sz[u]=1; son[u]=0;
    for(int i=Laxt[u];i;i=Next[i]){
         if(To[i]!=f&&!vis[To[i]]) {
            dfs1(To[i],u);
            sz[u]+=sz[To[i]];
            son[u]=max(son[u],sz[To[i]]);
         }
    }
    son[u]=max(son[u],all-son[u]);
    if(son[u]<son[rt]) rt=u;
}
void cal(int p)
{
    if(top==0) return ;int L=1,R=top-1,Mid;
    ans[p]=min(ans[p],getans(p,q[top]));
    while(L<=R){
        Mid=(L+R)>>1;
        ll tmp1=getans(p,q[Mid]),tmp2=getans(p,q[Mid+1]);
        if(tmp1<tmp2) R=Mid-1,ans[p]=min(ans[p],tmp1);
        else L=Mid+1,ans[p]=min(ans[p],tmp2);
    }
}
void get(int u,int f)
{
    cal(u);
    for(int i=Laxt[u];i;i=Next[i])
     if(To[i]!=f&&!vis[To[i]]) get(To[i],u);
}
bool check(int p){
    return (dis[p]-dis[q[top]])*(a[p]-a[q[top-1]])<=
     (dis[p]-dis[q[top-1]])*(a[p]-a[q[top]]);
}
void ADD(int p)
{
    if(top&&a[p]==a[q[top]]&&dis[p]<dis[q[top]]) top--;
    while(top>1&&check(p)) top--;
    q[++top]=p;
}
void get(int u,int f,ll D)
{
    dis[u]=D; sz[u]=1; S[++tot]=u;
    for(int i=Laxt[u];i;i=Next[i])
      if(To[i]!=f&&!vis[To[i]]){
         get(To[i],u,D+Len[i]);
         sz[u]+=sz[To[i]];
      }
}
void solve(int u,int f)
{
    vis[u]=1; dis[u]=0;
    top=0; tot=0; S[++tot]=u;
    for(int i=Laxt[u];i;i=Next[i]){
        if(!vis[To[i]]&&To[i]!=f)
           get(To[i],u,Len[i]);
    }
    sort(S+1,S+tot+1,cmp);
    rep(i,1,tot) ADD(S[i]);
    rep(i,1,tot) cal(S[i]);
    for(int i=Laxt[u];i;i=Next[i]){
        int v=To[i];
        if(!vis[v]&&v!=f) {
            all=sz[v]; rt=0;
            dfs1(v,0); solve(rt,0);
        }
    }
}
int main()
{
    int N,u,v,w;
    scanf("%d",&N); son[0]=N+1;
    rep(i,1,N) scanf("%lld",&a[i]);
    rep(i,1,N) ans[i]=a[i]*a[i];
    rep(i,1,N-1){
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w); add(v,u,w);
    }
    all=N; rt=0;
    dfs1(1,0); solve(rt,0);
    rep(i,1,N) sum+=ans[i];
    printf("%lld\n",sum);
    return 0;
}
View Code