Codeforces Round 857 (Div. 1)

一些fst的震撼。。。
A
看完题慌了一会,感觉很仙。但想到i和j两维可以分别占几位做,然后每维相同的格子都有两个,直接设为坐标异或起来就都是0了。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=205;
void work(){
    int n,m;
    cin>>n>>m;
    cout<<n*m<<endl;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            printf("%d ",i*256+j);
        }
        puts("");
    }
}
int main(){
    int T; cin>>T; while(T--) work();
    return 0;
}
B

原本没加这句话,如果set里没有不大于x的元素,就没法算到后面的最大值。
当时可能默认if里面都会做到,但其实两个if只是利用前面可选的优化答案而已,初始值肯定是要设的,这也和前面的情况形成对称。
以后应该多理顺一下每句话的意义,然后多考虑特殊情况!
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+5,inf=1e9+5;
int n;
struct node{
    int a,b;
}p[N];
bool cmp(node u,node v){
    return u.a<v.a;
}
int T,mxb[N];
void work(int id){
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&p[i].a,&p[i].b);
    }
    sort(p+1,p+n+1,cmp);
    mxb[n+1]=-inf;
    for(int i=n;i;i--) mxb[i]=max(mxb[i+1],p[i].b);
    multiset<int>st;
    st.clear();
    int ans=inf;
    for(int i=1;i<=n;i++){
        int x=p[i].a,t=inf;
        if(mxb[i+1]>=x) t=mxb[i+1]-x;
        else{
            t=x-mxb[i+1];
            multiset<int>:: iterator it=st.upper_bound(x);
            if(it!=st.end()) t=min(t,*it-x);
            if(it!=st.begin()){
                it--;
                t=min(t,x-max(mxb[i+1],*it));
            }
        }
        ans=min(ans,t);
        st.insert(p[i].b);
    }
    cout<<ans<<endl;
}
int main(){
    cin>>T; for(int i=1;i<=T;i++) work(i);
    return 0;
}
C
node里面放vector,并且cmp里参数没加&,会TLE!因为根据实验证明,sort不保证每个数的排序次数是均摊的,可能出现一些数排O(n)次的情况。

没加&的时候,排序时会把整个结构体复制出来排序,就寄了。加了&之后就只会比较一个数。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+5,inf=1e9+5;
int n;
struct node{
    vector<int>h;
    int sz,lt;
}p[N];
bool cmp(node u,node v){
    return u.lt<v.lt;
}
int dsc[N],mx[N],f[N],m;
void upd(int x,int y){
    while(x<=m) mx[x]=max(mx[x],y),x+=x&-x;
}
int cnt(int x){
    int mxa=0;
    while (x) mxa=max(mxa,mx[x]),x-=x&-x;
    return mxa;
}
int T;
void work(){
    cin>>n;
    m=0;
    for(int k,x,i=1;i<=n;i++){
        scanf("%d",&k);
        p[i].lt=p[i].sz=0;
        p[i].h.clear();
        while(k--){
            scanf("%d",&x);
            dsc[++m]=x;
            if(!p[i].lt || x>p[i].lt){
                p[i].lt=x;
                p[i].h.push_back(x);
                p[i].sz++;
            }
        }
    }
    sort(dsc+1,dsc+m+1);
    m=unique(dsc+1,dsc+m+1)-dsc;
    for(int i=1;i<=n;i++){
        for(int j=0;j<p[i].sz;j++) p[i].h[j]=lower_bound(dsc+1,dsc+m+1,p[i].h[j])-dsc;
        p[i].lt=p[i].h[p[i].sz-1];
    }
    sort(p+1,p+n+1,cmp);
    for(int i=1;i<=m;i++) mx[i]=0;
    int ans=0;
    for(int i=1;i<=n;i++){
        f[i]=0;
        for(int j=0;j<p[i].sz;j++){
            f[i]=max( f[i],cnt(p[i].h[j]-1)+p[i].sz-j);
        }
        upd(p[i].lt,f[i]);
        ans=max(ans,f[i]);
    }
    cout<<ans<<endl;
}
int main(){
    int T; cin>>T; while(T--) work();
    return 0;
}
D
注意到对于走过的一段城市,停下表演的城市肯定是像C题那样,是前缀最大值的城市;并且,每次都是在该城市买到恰好够到达下一个前缀最大值的城市。那么把每对点间的距离预处理一下,再按照{天数,剩下的钱}为优先级跑最短路即可。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=805,M=1e4+5;
const ll inf=1e15;
int n,m,p,w[N];
int hd[N],to[M],nx[M],wl[M],tt;
void add(int u,int v,int l){
    nx[++tt]=hd[u];
    to[hd[u]=tt]=v;
    wl[tt]=l;
   // cout<<"add:"<<u<<" "<<v<<" "<<hd[u]<<endl;
}
ll dis[N][N];
struct node{
    int u;
    ll t,r;
    bool operator <(const node& v) const{
        return t>v.t || (t==v.t && r<v.r);
    }
};
void find(int s){
    priority_queue<node>q;
    for(int i=1;i<=n;i++) dis[s][i]=inf;
    dis[s][s]=0;
    q.push((node){s,0,0});
    while(!q.empty()){
        node u=q.top();
        q.pop();
        //cout<<"u="<<u.u<<" "<<hd[u.u]<<endl;
        for(int e=hd[u.u];e;e=nx[e]){
            int v=to[e];
            //cout<<"vv="<<v<<endl;
            if(u.t+wl[e]<dis[s][v]){
                //cout<<"v="<<v<<endl;
                dis[s][v]=u.t+wl[e];
                q.push((node){v,dis[s][v],0});
            }
        }
    }
    //cout<<"s="<<s<<endl;
    //for(int i=1;i<=n;i++) cout<<dis[s][i]<<" "; puts("");
}
ll ds[N],rs[N];
void work(){
    cin>>n>>m>>p;
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    tt=0; memset(hd,0,sizeof(hd));
    while(m--){
        int u,v,l;
        scanf("%d%d%d",&u,&v,&l);
        add(u,v,l);
    }
    for(int i=1;i<=n;i++) find(i);
    priority_queue<node>q;
    for(int i=1;i<=n;i++) ds[i]=inf,rs[i]=0;
    ds[1]=0; rs[1]=p;
    q.push((node){1,0,p});
    while(!q.empty()){
        node u=q.top();
        q.pop();
        for(int v=1;v<=n;v++) if(v!=u.u && dis[u.u][v]<inf){
            ll k;
            if(u.r>=dis[u.u][v]) k=0;
            else k=(dis[u.u][v]-u.r-1)/w[u.u]+1;
            node vv=(node){v,u.t+k,(u.r+k*w[u.u])-dis[u.u][v]};
            if(vv.t<ds[v] || (vv.t==ds[v] && vv.r>rs[v])){
                ds[v]=vv.t;
                rs[v]=vv.r;
                q.push(vv);
            }
        }
    }
    if(ds[n]<inf) printf("%lld\n",ds[n]);
    else puts("-1");
}
int main(){
    int T; cin>>T; while(T--) work();
    return 0;
}
E
之后看了下,想了个树剖+分治+哈希+树状数组+启发式合并的做法,感觉很对,并对cf会出这种板套板的题目表示震撼,然而调了半天还没调出来。。。先占个坑
upd:补完了,一些6k的震撼。。。

思路其实挺简单的,就五个板子套在一起:
1.先树剖把路径转化成log段区间,就变成序列上的问题。
2.发现是个合并集合的过程,则有效的合并次数是\(O(n)\)的。考虑如何在序列上的两段等长序列中,用\(O(对应位置所属集合不同的对数\times log)\)找出,对区间分治即可。
3.上述做法需要判断并查集中fa数组的两段区间是否相等,故用哈希。
4.并查集需要修改并且实时维护每个点所属集合,故用启发式合并。
5.单点修改区间查询,故用树状数组。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const ll B=793999;
const int N=4e5+5,P=1e9+7;
inline int M(int x,int y){
    return 1ll*x*y%P;
}
inline int fpw(int a,int x){
    int s=1;
    for(;x;x>>=1,a=1ll*a*a%P) if(x&1) s=1ll*s*a%P;
    return s;
}
ll pw[N];
int n,dn[N],fa[N],mx[N],mn[N],sz[N],ans=1;
vector<int>sn[N];
struct interval{
    int l,r;
    int lenth(){
        return abs(r-l)+1;
    }
    void print(){
        cout<<"interval:"<<l<<" "<<r<<endl;
    }
    /*bool operator < (const interval& u) const {
        return l<u.l;
    }*/
};
struct BIT{
    ll A[N];
    inline void init(){
        for(int i=1;i<=n;i++) A[i]=0;
    }
    inline void upd(int x,ll y){
        //printf("upd: x=%d y=%d\n",x,y);
        while(x<=n) A[x]+=y,x+=x&-x;//cout<<x<<endl;
    }
    inline ll cnt(int x){
        ll s=0;
        while(x) s+=A[x],x-=x&-x;//cout<<x<<endl;
        return s;
    }
}bit[2];
void init(){
    pw[0]=1;
    //cout<<"n="<<n<<endl;
    for(int i=1;i<=n;i++) scanf("%d%d",&mx[dn[i]],&mn[dn[i]]);
    for(int i=1;i<=n;i++){
        pw[i]=pw[i-1]*B;
        fa[i]=i;
        sz[i]=1;
        ans=M(ans,mn[i]-mx[i]+1);
        //cout<<mx[i]<<" "<<mn[i]<<endl;
        //cout<<i<<" "<<ans<<endl;
        sn[i].push_back(i);
    }
    for(int i=1;i<=n;i++) {
        bit[0].upd(i, pw[i] * i);
        bit[1].upd(i, pw[n - i + 1] * i);
    }
}
void mdf(int u,int x){
   // cout<<"mdf: u="<<u<<" fa="<<fa[u]<<" x="<<x<<endl;
    bit[0].upd( u,pw[u]*(x-fa[u]) );
    bit[1].upd( u,pw[n-u+1]*(x-fa[u]) );
    fa[u]=x;
    sn[x].push_back(u);
}
void merge(int u,int v){
    //cout<<"!:"<<u<<" "<<v<<endl;
    u=fa[u]; v=fa[v];
    if(u==v) return;
   // cout<<"merge: u="<<u<<" v="<<v<<endl;
    if(sz[u]<sz[v]) swap(u,v);
    ans=M(ans,fpw(max(0,mn[u]-mx[u]+1),P-2));
    ans=M(ans,fpw(max(0,mn[v]-mx[v]+1),P-2));
    for(int i=0;i<sn[v].size();i++){
        int x=sn[v][i];
        mdf(x,u);
    }
    mx[u]=max(mx[u],mx[v]);
    mn[u]=min(mn[u],mn[v]);
    //cout<<mn[u]<<" "<<mx[u]<<endl;
    sz[u]+=sz[v];
    ans=M(ans,max(0,mn[u]-mx[u]+1));
}
ll cnt(interval u){
    //cout<<"cnt:"<<bit.cnt(u.r)<<" "<<bit.cnt(u.l-1)<<endl;
    if(u.l<u.r) return (bit[0].cnt(u.r)-bit[0].cnt(u.l-1))*pw[n-u.r];
    else return (bit[1].cnt(u.l)-bit[1].cnt(u.r-1))*pw[u.r-1];
}
bool chk(interval u,interval v){
    // puts("chk");
    // u.print(); v.print();
    //cout<<cnt(u)<<" "<<pw[v.l-u.l]<<" "<<cnt(v)<<endl;
    //cout<<cnt(u)<<" "<<cnt(v)<<endl;
    return cnt(u)==cnt(v);
}
interval get(interval& u,int k){
    if(u.l<u.r){
        int tmp=u.l;
        u.l+=k;
        return (interval){tmp,tmp+k-1};
    }
    else{
        int tmp=u.l;
        u.l-=k;
        return (interval){tmp,tmp-k+1};
    }
}
void work(interval u,interval v,int len){
    //if(u.l>v.l) swap(u,v);
    if(chk(u,v)) return;
    //cout<<"len="<<len<<endl;
    // printf("work:len=%d\n",len);
    // u.print(); v.print();
    // puts("");
    if(len==1){
        merge(u.r,v.r);
        return;
    }
    int hlf=len/2;
    work(get(u,hlf),get(v,hlf),hlf);
    work(u,v,len-hlf);
}
struct WeightDivide{
    int fa[N],dp[N],sz[N],sn[N],tp[N],ct;
    int hd[N],to[N<<1],nx[N<<1],tt;
    inline void add(int u,int v){
        nx[++tt]=hd[u];
        to[hd[u]=tt]=v;
    }
    void dfs1(int u){
        sz[u]=1;
        for(int e=hd[u];e;e=nx[e]){
            int v=to[e];
            dp[v]=dp[u]+1;
            dfs1(v);
            sz[u]+=sz[v];
            if(sz[v]>sz[sn[u]]) sn[u]=v;
        }
    }
    void dfs2(int u){
        dn[u]=++ct;
        if(sn[u]) tp[sn[u]]=tp[u],dfs2(sn[u]);
        for(int e=hd[u];e;e=nx[e]){
            int v=to[e];
            if(v==sn[u]) continue;
            tp[v]=v;
            dfs2(v);
        }
    }
    void init(){
        fa[1]=tt=ct=0;
        for(int i=0;i<=n;i++) hd[i]=sz[i]=sn[i]=tp[i]=0;
        for(int i=2;i<=n;i++) scanf("%d",&fa[i]),add(fa[i],i);
        dfs1(1);
        dfs2(1);
    }
    int lca(int u,int v){
        while(tp[u]!=tp[v]){
            if(dp[tp[u]]<dp[tp[v]]) swap(u,v);
            u=fa[tp[u]];
        }
        if(dn[u]>dn[v]) swap(u,v);
        return u;
    }
    vector<interval> find(int u,int v){
       // cout<<"find:"<<u<<" "<<v<<endl;
        vector<interval>h; h.clear();
        int w=lca(u,v);
        while(tp[u]!=tp[w]){
            h.push_back((interval){dn[u],dn[tp[u]]});
            u=fa[tp[u]];
        }
        h.push_back((interval){dn[u],dn[w]});
        stack<interval>q;
        while(tp[v]!=tp[w]){
            q.push((interval){dn[tp[v]],dn[v]});
            v=fa[tp[v]];
        }
        if(v!=w) q.push((interval){dn[w]+1,dn[v]});
        while(!q.empty()) h.push_back(q.top()),q.pop();
        //for(int i=0;i<h.size();i++) h[i].print();
        return h;
    }
}T;
int main(){
    cin>>n;
    bit[0].init();
    bit[1].init();
    T.init();
    init();
    int m;
    cin>>m;
    // cout<<"m="<<m<<endl;
    // cout<<ans<<endl;
    while(m--){
        int a,b,c,d;
        scanf("%d%d%d%d",&a,&b,&c,&d);
        //cout<<"m="<<m<<endl;
        vector<interval>u=T.find(a,b),v=T.find(c,d);
        for(int i=0,j=0;i<u.size() && j<v.size();){
            int lu=u[i].lenth(),lv=v[j].lenth();
            //cout<<"lu="<<lu<<" lv="<<lv<<endl;
            //print()
            if(lu<lv){
                work(u[i],get(v[j],lu),lu);
                i++;
            }
            else if(lu==lv){
                //puts("!");
                work(u[i],v[j],lu);
                i++; j++;
            }
            else{
                work(v[j],get(u[i],lv),lv);
                j++;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
F
考虑暴力DP,f[i][j]表示前i个,乘积为j的最大价值;考虑优化后者,发现对于一段的j是等价的,我们关心的是剩下的乘积至少要多少,那就把状态改为这个;每次转移就是枚举当前维的切分数,然后取上整,而这个可以用整出分块优化,每块取左端点即可;这样状态数是\(O(n \sqrt{k} )\),转移是\(O(n k^{ \frac{3}{4} } )\)的。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e7+5,M=10005;
struct node{
    int l,x;
}p[N];
//vector<node>
int n,k,t,a[N];
int id[N],num[N];
double f[105][M];
int main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    int m=k-1;
    for(int i=1;i<=m+1;){
        int x=m/i,j;
        if(x) j=m/x;
        else j=i;
        node u=(node){i,x+1};
        p[++t]=u;
        id[x+1]=t;
        num[t]=x+1;
        //cout<<i<<" "<<j<<endl;
        i=j+1;
    }
    for(int i=1;i<=t;i++) f[0][i]=0;
    f[0][id[k]]=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=t;j++){
            int m=num[j]-1;
            //cout<<"m="<<m<<endl;
            for(int u=1;u<=m+1;){
                int x=m/u,v;
                if(x) v=m/x;
                else v=u;
                //cout<<u<<" "<<v<<endl;
                f[i][id[x+1]]=max(f[i][id[x+1]],f[i-1][j]*(a[i]/u)/a[i]);
                u=v+1;
            }
        }
    }
    double ans=f[n][id[1]]*k;
    if(fabs(ans)<1e-9) puts("0");
    else printf("%.12lf\n",ans);
    return 0;
}
 
                    
                 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号