POI2012题解

POI2012题解

这次的完整的\(17\)道题哟。

[BZOJ2788][Poi2012]Festival

很显然可以差分约束建图。这里问的是变量最多有多少种不同的取值。

我们知道,在同一个强连通分量中的变量的相对大小是限制死了的,即这个强连通分量中的最大值减去最小值不为\(\inf\),而这个区间中的所有数一定都可以被取到(因为这里的边权只有\(0,\pm1\)嘛),所以一个强连通分量对答案的贡献是这个强连通分量中的最长路\(+1\)。对于不在同一个强连通分量中的变量,其相对大小不受限制,取值一定可以做到无交集,所以答案就是各强连通分量的答案之和。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 605;
int n,m1,m2,a[N][N],to[N*N],nxt[N*N],head[N],cnt,dfn[N],low[N],tim,vis[N],S[N],bel[N],scc,ans;
void Tarjan(int u){
    dfn[u]=low[u]=++tim;vis[S[++S[0]]=u]=1;
    for (int e=head[u];e;e=nxt[e])
        if (!dfn[to[e]]) Tarjan(to[e]),low[u]=min(low[u],low[to[e]]);
        else if (vis[to[e]]) low[u]=min(low[u],dfn[to[e]]);
    if (dfn[u]==low[u]){
        ++scc;int x=0;
        do x=S[S[0]--],vis[x]=0,bel[x]=scc;while (x^u);
    }
}
int main(){
    n=gi();m1=gi();m2=gi();
    memset(a,63,sizeof(a));
    for (int i=1;i<=n;++i) a[i][i]=0;
    while (m1--){
        int u=gi(),v=gi();
        a[u][v]=min(a[u][v],1);a[v][u]=-1;
        to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
        to[++cnt]=u;nxt[cnt]=head[v];head[v]=cnt;
    }
    while (m2--){
        int u=gi(),v=gi();a[v][u]=min(a[v][u],0);
        to[++cnt]=u;nxt[cnt]=head[v];head[v]=cnt;
    }
    for (int i=1;i<=n;++i) if (!dfn[i]) Tarjan(i);
    for (int t=1;t<=scc;++t){
        for (int k=1;k<=n;++k)
            if (bel[k]==t)
                for (int i=1;i<=n;++i)
                    if (bel[i]==t)
                        for (int j=1;j<=n;++j)
                            if (bel[j]==t)
                                a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
        int md=0;
        for (int i=1;i<=n;++i)
            if (bel[i]==t)
                for (int j=1;j<=n;++j)
                    if (bel[j]==t)
                        md=max(md,a[i][j]);
        ans+=md+1;
    }
    for (int i=1;i<=n;++i) if (a[i][i]<0) return puts("NIE"),0;
    printf("%d\n",ans);return 0;
}

[BZOJ2789][Poi2012]Letters

醒醒,交换次数就是逆序对数。

然后同种字母之间的相对顺序不会改变(你去交换两个相邻的同种字母试试),所以就给字符标个号然后求逆序对即可。

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 1e6+5;
int n,nxt[N],hd[26],c[N];char s1[N],s2[N];long long ans;
void mdf(int k){while(k<=n)++c[k],k+=k&-k;}
int qry(int k){int s=0;while(k)s+=c[k],k^=k&-k;return s;}
int main(){
    scanf("%d%s%s",&n,s1+1,s2+1);
    for (int i=1,c;i<=n;++i) c=s1[i]-'A',nxt[i]=hd[c],hd[c]=i;
    for (int i=n,c;i;--i) c=s2[i]-'A',ans+=qry(hd[c]),mdf(hd[c]),hd[c]=nxt[hd[c]];
    printf("%lld\n",ans);return 0;
}

[BZOJ2790][Poi2012]Distance

对于两个数\(a,b\),他们之间的距离函数\(d(a,b)=f(a)+f(b)-2f(\gcd(a,b))\),其中\(f(i)\)\(i\)中包含的质因子个数。我们枚举\(\gcd\)再找出所有是\(\gcd\)倍数的数,显然只会用\(f(a)\)最小的\(a\)与其余的\(b\)去匹配。

可能枚举到的\(\gcd\)并不是真正的\(\gcd(a,b)\),但可以保证此时算出的答案不会更优而使答案最优的\(\gcd(a,b)\)一定会被枚举到。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 1e6+5;
int n,zhi[N],pri[N],tot,s[N],a[N],nxt[N],hd[N],q[N],top,ans[N],pos[N],las[N];
int main(){
    memset(ans,63,sizeof(ans));
    for (int i=2;i<N;++i){
        if (!zhi[i]) pri[++tot]=i,s[i]=1;
        for (int j=1;i*pri[j]<N;++j){
            zhi[i*pri[j]]=1;s[i*pri[j]]=s[i]+1;
            if (i%pri[j]==0) break;
        }
    }
    n=gi();
    for (int i=1;i<=n;++i) a[i]=gi(),nxt[i]=hd[a[i]],hd[a[i]]=i;
    for (int i=1;i<N;++i){
        top=0;
        for (int j=i;j<N;j+=i)
            for (int k=hd[j];k;k=nxt[k])
                q[++top]=k;
        if (top<=1) continue;int p=q[1];
        for (int j=2;j<=top;++j)
            if (s[a[q[j]]]<s[a[p]]||(s[a[q[j]]]==s[a[p]]&&q[j]<p)) swap(p,q[j]);
        for (int j=2;j<=top;++j){
            int val=s[a[q[j]]]+s[a[p]]-(s[i]<<1);
            if (val<ans[q[j]]||(val==ans[q[j]]&&p<pos[q[j]])) ans[q[j]]=val,pos[q[j]]=p;
            if (val<ans[p]||(val==ans[p]&&q[j]<pos[p])) ans[p]=val,pos[p]=q[j];
        }
    }
    for (int i=1;i<=n;++i) printf("%d\n",pos[i]);
    return 0;
}

[BZOJ2791][Poi2012]Rendezvous

虽然有一大堆条件,但实际上\(x,y\)只有至多两种取值。(在环上\(a\)走到\(b\),或者\(b\)走到\(a\)\(a,b\)同时走一定不优)

如果在同一棵基环内向树上的话就是走到树上\(\mbox{lca}\)

#include<cstdio>
#include<algorithm>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 5e5+5;
int n,m,fa[N],vis[N],S[N],ins[N],cir[N],pos[N],len[N],id,nxt[N],hd[N],dep[N],sz[N],bel[N],top[N];
void dfs(int u){
    if (ins[u]){
        ++id;
        for (int i=S[0];S[i+1]!=u;--i)
            cir[S[i]]=id,pos[S[i]]=++len[id];
        return;
    }
    if (vis[u]) return;vis[u]=1;
    ins[S[++S[0]]=u]=1;dfs(fa[u]);ins[u]=S[S[0]--]=0;
}
void dfs1(int u,int f,int rt){
    dep[u]=dep[f]+1;sz[u]=1;bel[u]=rt;
    for (int v=hd[u];v;v=nxt[v])
        dfs1(v,u,rt),sz[u]+=sz[v];
}
void dfs2(int u,int up){
    top[u]=up;int son=0;
    for (int v=hd[u];v;v=nxt[v]) son=sz[v]>sz[son]?v:son;
    if (son) dfs2(son,up);else return;
    for (int v=hd[u];v;v=nxt[v]) if (v^son) dfs2(v,v);
}
int lca(int u,int v){
    while (top[u]^top[v]){
        if (dep[top[u]]>dep[top[v]]) u=fa[top[u]];
        else v=fa[top[v]];
    }
    return dep[u]<dep[v]?u:v;
}
bool cmp(int x1,int y1,int x2,int y2){
    if (max(x1,y1)^max(x2,y2)) return max(x1,y1)<max(x2,y2);
    if (min(x1,y1)^min(x2,y2)) return min(x1,y1)<min(x2,y2);
    return x1>=y1;
}
int main(){
    n=gi();m=gi();dep[0]=-1;
    for (int i=1;i<=n;++i) fa[i]=gi();
    for (int i=1;i<=n;++i) if (!vis[i]) dfs(i);
    for (int i=1;i<=n;++i) if (!cir[i]) nxt[i]=hd[fa[i]],hd[fa[i]]=i;
    for (int i=1;i<=n;++i) if (cir[i]) dfs1(i,0,i),dfs2(i,i);
    while (m--){
        int u=gi(),v=gi();
        if (cir[bel[u]]^cir[bel[v]]) puts("-1 -1");
        else if (bel[u]==bel[v]){
            int w=lca(u,v);
            printf("%d %d\n",dep[u]-dep[w],dep[v]-dep[w]);
        }else{
            int c1=(pos[bel[u]]-pos[bel[v]]+len[cir[bel[u]]])%len[cir[bel[u]]];
            int c2=len[cir[bel[u]]]-c1;
            if (cmp(dep[u]+c1,dep[v],dep[u],dep[v]+c2)) printf("%d %d\n",dep[u]+c1,dep[v]);
            else printf("%d %d\n",dep[u],dep[v]+c2);
        }
    }
    return 0;
}

[BZOJ2792][Poi2012]Well

肯定是二分答案。先不考虑那个\(0\),可以贪心地减,具体来说就是从前往后如果\(a_i>a_{i-1}+mid\)就把\(a_i\)减成\(a_{i-1}+mid\),从后往前如果\(a_i>a_{i+1}+mid\)就把\(a_i\)减成\(a_{i+1}+mid\)

现在考虑把一个位置改成\(0\),相当于是从这个点开始向左向右画两条斜率为\(\pm mid\)的直线,在这条直线上方的点都要减。考虑求出把每个位置改成\(0\)后影响的区间左右端点,这个一定是随下标增长单调的,所以就可以线性计算出把每个位置改成\(0\)所需的额外代价。

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
ll gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const ll N = 1e6+5;
int n;ll m,a[N],b[N],sum[N],cost[N];
int check(ll mid){
    ll tot=0;
    for (int i=1;i<=n;++i) b[i]=a[i];
    for (int i=2;i<=n;++i) if (b[i]>b[i-1]+mid) tot+=b[i]-b[i-1]-mid,b[i]=b[i-1]+mid;
    for (int i=n-1;i;--i) if (b[i]>b[i+1]+mid) tot+=b[i]-b[i+1]-mid,b[i]=b[i+1]+mid;
    for (int i=1;i<=n;++i) sum[i]=sum[i-1]+b[i];
    for (int i=n,l=n;i;--i){
        while (l&&b[l]>mid*(i-l)) --l;
        cost[i]=sum[i]-sum[l]-(mid*(i-l-1)*(i-l)>>1);
    }
    for (int i=1,r=1;i<=n;++i){
        while (r<=n&&b[r]>mid*(r-i)) ++r;
        if (tot+cost[i]+sum[r-1]-sum[i]-(mid*(r-i-1)*(r-i)>>1)<=m) return i;
    }
    return 0;
}
int main(){
    n=gi();scanf("%lld",&m);
    for (int i=1;i<=n;++i) a[i]=gi();
    int l=0,r=1<<30,res;
    while (l<=r){
        int mid=l+r>>1;
        if (check(mid)) res=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d %lld\n",check(res),res);return 0;
}

[BZOJ2793][Poi2012]Vouchers

维护每种人数最后一个取到哪里了,直接暴力,复杂度是调和级数。

#include<cstdio>
#include<algorithm>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 1e6+5;
int n,m,mrk[N],hd[N],vis[N],tot;
long long sum,ans[N];
int main(){
    m=gi();while (m--) mrk[gi()]=1;
    for (int i=1;i<N;++i) hd[i]=i;
    n=gi();while (n--){
        int x=gi();long long now=sum;
        while (now<sum+x){
            while (hd[x]<N&&vis[hd[x]]) hd[x]+=x;
            if (hd[x]>=N) break;
            vis[hd[x]]=1;++now;if (mrk[hd[x]]) ans[++tot]=now;
        }
        sum+=x;
    }
    printf("%d\n",tot);
    for (int i=1;i<=tot;++i) printf("%lld\n",ans[i]);
    return 0;
}

[BZOJ2794][Poi2012]Cloakroom

询问离线,按\(a_i\)值加入背包,背包维护\(f_i\)表示所有凑出\(i\)的方案中\(b_i\)值最小值的最大值。

转移比较清奇,\(f_i=\max\{f_i,\min\{f_i-c,b\}\}\)。初值\(f_0=\inf,f_i=-1(i>0)\)

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 1e6+5;
int n,m,f[N],ans[N];
struct node{
    int c,a,b;
    bool operator < (const node &x) const
        {return a<x.a;}
}p[N];
struct query{
    int m,k,s,id;
    bool operator < (const query &x) const
        {return m<x.m;}
}q[N];
void insert(int c,int b){
    for (int i=100000;i>=c;--i)
        if (~f[i-c]) f[i]=max(f[i],min(f[i-c],b));
}
int main(){
    n=gi();
    for (int i=1;i<=n;++i) p[i]=(node){gi(),gi(),gi()};
    m=gi();
    for (int i=1;i<=m;++i) q[i]=(query){gi(),gi(),gi(),i};
    sort(p+1,p+n+1);sort(q+1,q+m+1);
    memset(f,-1,sizeof(f));f[0]=1<<30;
    for (int i=1,j=1;i<=m;++i){
        while (j<=n&&p[j].a<=q[i].m) insert(p[j].c,p[j].b),++j;
        ans[q[i].id]=f[q[i].k]>q[i].m+q[i].s;
    }
    for (int i=1;i<=m;++i) puts(ans[i]?"TAK":"NIE");
    return 0;
}

[BZOJ2795][Poi2012]A Horrible Poem

字符串\(\mbox{hash}\)。有一种\(O(q\sqrt n)\)枚举约数的做法,然后每次尝试减去一个质因数就可以做到\(O(q\log n)\)

#include<cstdio>
#include<algorithm>
using namespace std;
#define ull unsigned long long
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 5e5+5;
const int base = 20020415;
int n,q,pri[N],tot,zhi[N],p[N];
char s[N];ull pw[N],hsh[N];
ull cal(int l,int r){return hsh[r]-hsh[l-1]*pw[r-l+1];}
bool check(int l,int r,int sz){return cal(l,r-sz)==cal(l+sz,r);}
int main(){
    for (int i=pw[0]=1;i<N;++i) pw[i]=pw[i-1]*base;
    n=gi();scanf("%s",s+1);q=gi();
    for (int i=1;i<=n;++i) hsh[i]=hsh[i-1]*base+s[i];
    for (int i=2;i<=n;++i){
        if (!zhi[i]) pri[++tot]=i,p[i]=i;
        for (int j=1;j<=tot&&i*pri[j]<=n;++j){
            zhi[i*pri[j]]=1;p[i*pri[j]]=pri[j];
            if (i%pri[j]==0) break;
        }
    }
    while (q--){
        int l=gi(),r=gi(),ans=r-l+1;
        for (int i=r-l+1;i>1;i/=p[i])
            if (check(l,r,ans/p[i])) ans/=p[i];
        printf("%d\n",ans);
    }
    return 0;
}

[BZOJ2796][Poi2012]Fibonacci Representation

每次贪心减去与当前\(n\)值绝对值之差最小的\(\mbox{Fibonacci}\)数即可。不太会证。

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
ll f[1000];int len;
int cal(ll n){
    if (n==0) return 0;
    int x=lower_bound(f+1,f+len+1,n)-f;
    return cal(min(n-f[x-1],f[x]-n))+1;
}
int main(){
    f[1]=1;f[len=2]=2;
    while (f[len]<=1e18) ++len,f[len]=f[len-1]+f[len-2];
    int T;scanf("%d",&T);while (T--){
        ll n;scanf("%lld",&n);printf("%d\n",cal(n));
    }
    return 0;
}

[BZOJ2797][Poi2012]Squarks

前几天\(\mbox{NOIP}\)模拟赛的一个题。考场上当然不会做啦

假设\(x_1\le x_2\le ... \le x_n\),如果已知\(x_1\),因为\(x_1+x_2\)一定是这\(\binom n2\)个数中最小的,所以就可以知道\(x_2\),继而可以知道\(x_3\),因为\(x_1+x_3\)一定是次小的。接着去掉\(x_2+x_3\)后,\(x_1+x_4\)又是最小的,然后就又知道了\(x_4\)。。。以此类推便可以解出所有的\(x_i\)

所以现在的瓶颈在于枚举\(x_1\)。因为\(x_1+x_2,x_1+x_3\)一定是最小的两个数,而且如果我们知道了\(x_2+x_3\)就可以把这三个数都解出来,所以可以枚举\(x_2+x_3\)是第几个数。只需要从第\(3\)个枚举到第\(n\)个即可,因为比\(x_2+x_3\)还小的一定是\(x_1+x_i\),这样的数只有\(n-1\)个。

#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 305;
int n,m,a[N*N],tmp[N],len,x[N],ans[N][N],tot;
multiset<int>S,S0;
void work(int x1){
    S=S0;x[1]=x1;x[2]=*S.begin()-x1;S.erase(S.begin());
    if (x[2]<x[1]) return;
    for (int i=3;i<=n;++i){
        x[i]=*S.begin()-x1;S.erase(S.begin());
        if (x[i]<x[i-1]) return;
        for (int j=2;j<i;++j){
            if (S.find(x[j]+x[i])==S.end()) return;
            S.erase(S.find(x[j]+x[i]));
        }
    }
    ++tot;
    for (int i=1;i<=n;++i) ans[tot][i]=x[i];
}
int main(){
    n=gi();m=n*(n-1)>>1;
    for (int i=1;i<=m;++i) S0.insert(a[i]=gi());sort(a+1,a+m+1);
    for (int i=3;i<=n;++i){
        int x=a[1]+a[2]-a[i];
        if (x<=0||(x&1)) continue;
        tmp[++len]=x>>1;
    }
    len=unique(tmp+1,tmp+len+1)-tmp-1;
    for (int i=1;i<=len;++i) work(tmp[i]);
    printf("%d\n",tot);
    for (int i=1;i<=tot;++i,puts(""))
        for (int j=1;j<=n;++j)
            printf("%d ",ans[i][j]);
    return 0;
}

[BZOJ2798][Poi2012]Bidding

口胡一下好了。

考虑第一维,它一定是\(2^a3^b\)的形式。所以总状态数只有\(O(n\log^2n)\),记忆化搜索一下就好了吧。

不很懂为什么要强行把博弈出成交互。

[BZOJ2799][Poi2012]Salaries

可以求出每个点最大的可以取到的权值,然后问题就变成了,给你若干个数,每个数可以取前\(b_i\)大的权值,求每个数是否可以确定取那个值。

直接按照\(b_i\)排序然后权值从小往大处理就好了。

#include<cstdio>
#include<algorithm>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 1e6+5;
int n,m,rt,a[N],vis[N],nxt[N],head[N],mx[N];
pair<int,int>p[N];
void dfs(int u,int x){
    if (!a[u]) p[++m]=make_pair(x,u);
    for (int v=head[u];v;v=nxt[v]) dfs(v,a[v]?a[v]:mx[x-1]);
}
int main(){
    n=gi();
    for (int i=1,f;i<=n;++i){
        f=gi(),a[i]=gi();vis[a[i]]=1;
        if (f^i) nxt[i]=head[f],head[f]=i;
        else rt=i;
    }
    for (int i=1;i<=n;++i) mx[i]=vis[i]?mx[i-1]:i;
    dfs(rt,n);sort(p+1,p+m+1);
    for (int i=1,j=0,k=0;i<=n;++i)
        if (vis[i]) ++k;
        else{
            int t=0;
            while (j<m&&p[j+1].first==i) ++j,++t;
            if (t==1&&j+k==i) a[p[j].second]=i;
        }
    for (int i=1;i<=n;++i) printf("%d\n",a[i]);
    return 0;
}

[BZOJ2800][Poi2012]Leveling Ground

把原序列差分,变成\(n+1\)个数,每次操作相当于是选两个数一个加\(a\)一个减\(a\)或者一个加\(b\)一个减\(b\)

\(i\)位置上加了\(x_i\)\(a\),加了\(y_i\)\(b\),于是就有\(ax_i+by_i=val_i\)(假设\(val_i\)是差分后这个位置上的值)。上\(\mbox{exgcd}\),然后我们要最小化\(|x_i|+|y_i|\)。因为\(val_i\)可能取负值,所以\(|x_i|+|y_i|\)取最小的位置有:\(x\)取最小非负整数解或最大非正整数解,\(y\)取最小非负整数解或最大非正整数解。

取完之后可能\(\sum x_i\neq0\),所以还要把一些\(x_i\)\(y_i\)均摊一下,可以开个堆,每次选择变化量最小的一种方式去修改\(x_i\)\(y_i\)的值。

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
#define ll long long
#define pi pair<ll,int>
const int N = 1e5+5;
int n,A,B,D,X,Y,val[N],ansx[N],ansy[N];
priority_queue<pi,vector<pi>,greater<pi> >Q;
void exgcd(int a,int b,int &x,int &y){
    if (!b) x=1,y=0;
    else exgcd(b,a%b,y,x),y-=a/b*x;
}
int abs(int x){return x>0?x:-x;}
ll cost(int i){
    return 1ll*abs(ansx[i]-B)+abs(ansy[i]+A)-abs(ansx[i])-abs(ansy[i]);
}
int main(){
    n=gi()+1;A=gi();B=gi();D=__gcd(A,B);A/=D;B/=D;exgcd(A,B,X,Y);
    for (int i=1;i<n;++i) val[i]=gi();
    for (int i=n;i;--i){
        val[i]-=val[i-1];
        if (val[i]%D) return puts("-1"),0;val[i]/=D;
        int x=(1ll*X*val[i]%B+B)%B,y=(val[i]-1ll*A*x)/B;
        ansx[i]=x,ansy[i]=y;
        x-=B,y+=A;
        if (abs(x)+abs(y)<abs(ansx[i])+abs(ansy[i]))
            ansx[i]=x,ansy[i]=y;
        y=(1ll*Y*val[i]%A+A)%A,x=(val[i]-1ll*B*y)/A;
        if (abs(x)+abs(y)<abs(ansx[i])+abs(ansy[i]))
            ansx[i]=x,ansy[i]=y;
        y-=A,x+=B;
        if (abs(x)+abs(y)<abs(ansx[i])+abs(ansy[i]))
            ansx[i]=x,ansy[i]=y;
    }
    ll t=0;
    for (int i=1;i<=n;++i) t+=ansx[i];
    t/=B;if (t<0){
        t=-t;swap(A,B);swap(X,Y);
        for (int i=1;i<=n;++i) swap(ansx[i],ansy[i]);
    }
    for (int i=1;i<=n;++i) Q.push(make_pair(cost(i),i));
    while (t--){
        int i=Q.top().second;Q.pop();
        ansx[i]-=B;ansy[i]+=A;Q.push(make_pair(cost(i),i));
    }
    t=0;
    for (int i=1;i<=n;++i) t+=abs(ansx[i])+abs(ansy[i]);
    printf("%lld\n",t>>1);return 0;
}

[BZOJ2801][Poi2012]Minimalist Security

对于每个连通块,设其中某一个点的权值减少量为\(x\),那么其他所有点的权值减少量都可以用\(k_ix+b_i\)表示。这样也可以求出\(x\)的取值范围。\(dfs\)过程中遇到返祖边解个方程即可。

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 5e5+5;
const int M = 3e6+5;
int n,m,to[M<<1],nxt[M<<1],head[N],cnt;
ll p[N],ww[M<<1],k[N],b[N],low,upp,sumk,sumb,ans_min,ans_max;
void link(int u,int v,ll w){
    to[++cnt]=v;nxt[cnt]=head[u];ww[cnt]=w;head[u]=cnt;
    to[++cnt]=u;nxt[cnt]=head[v];ww[cnt]=w;head[v]=cnt;
}
void dfs(int u){
    if (k[u]==1) low=max(low,-b[u]),upp=min(upp,p[u]-b[u]);
    else low=max(low,b[u]-p[u]),upp=min(upp,b[u]);
    sumk+=k[u];sumb+=b[u];
    for (int e=head[u],v;e;e=nxt[e])
        if (!k[v=to[e]]){
            k[v]=-k[u];b[v]=-b[u]+p[u]+p[v]-ww[e];
            dfs(v);
        }else{
            if (k[u]!=k[v]){
                if (b[u]+b[v]!=p[u]+p[v]-ww[e]) puts("NIE"),exit(0);
            }else{
                ll tmp=p[u]+p[v]-ww[e]-b[u]-b[v];
                if (tmp&1) puts("NIE"),exit(0);
                tmp=(tmp>>1)*k[u];
                if (low>tmp||upp<tmp) puts("NIE"),exit(0);
                low=upp=tmp;
            }
        }
}
int main(){
    n=gi();m=gi();
    for (int i=1;i<=n;++i) p[i]=gi();
    for (int i=1;i<=m;++i){
        int u=gi(),v=gi();ll w=gi();
        link(u,v,w);
    }
    for (int i=1;i<=n;++i)
        if (!k[i]){
            k[i]=1;b[i]=0;sumk=sumb=0;
            low=-1e18;upp=1e18;
            dfs(i);
            if (low>upp) puts("NIE"),exit(0);
            if (sumk>=0){
                ans_min+=1ll*sumk*low+sumb;
                ans_max+=1ll*sumk*upp+sumb;
            }else{
                ans_min+=1ll*sumk*upp+sumb;
                ans_max+=1ll*sumk*low+sumb;
            }
        }
    printf("%lld %lld\n",ans_min,ans_max);return 0;
}

[BZOJ2802][Poi2012]Warehouse Store

贪心,能选则选,否则如果当天的量比之前选过的量最多的那天的量要少就不选那天改选当天。

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 250005;
int n,a[N],vis[N],ans;long long sum;
priority_queue<pair<int,int> >Q;
int main(){
    n=gi();
    for (int i=1;i<=n;++i) a[i]=gi();
    for (int i=1;i<=n;++i){
        int b=gi();sum+=a[i];
        if (sum>=b) sum-=b,Q.push(make_pair(b,i)),vis[i]=1;
        else if (!Q.empty()&&b<Q.top().first&&sum+Q.top().first>=b){
            sum+=Q.top().first,vis[Q.top().second]=0,Q.pop();
            sum-=b,Q.push(make_pair(b,i)),vis[i]=1;
        }
    }
    for (int i=1;i<=n;++i) if (vis[i]) ++ans;
    printf("%d\n",ans);
    for (int i=1;i<=n;++i) if (vis[i]) printf("%d ",i);
    puts("");return 0;
}

[BZOJ2803][Poi2012]Prefixuffix

首先可以发现就是求把原串分成\(ABCBA\)这样的形式后最大的\(|A|+|B|\)\(A,B,C\)是三个字符串,可能为空串)。一个好证但不容易发现的结论是:假设对于长度为\(i\)\(A\)串其对应的最大的\(B\)串长度是\(f_i\),则一定有\(f_i\le f_{i+1}+2\)。所以直接暴力就好了。

此题卡\(\mbox{Hash}\)。写了双模数才过的。

#include<cstdio>
#include<algorithm>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
#define ll long long
const int N = 1e6+5;
const ll base = 20020415;
int n,ans;char s[N];
struct Hash{
    ll mod,hsh[N],pw[N];
    void work(){
        for (int i=pw[0]=1;i<=n;++i) pw[i]=pw[i-1]*base%mod;
        for (int i=1;i<=n;++i) hsh[i]=(hsh[i-1]*base+s[i])%mod;
    }
    ll cal(int i,int k){
        return (hsh[i]-hsh[i-k]*pw[k]%mod+mod)%mod;
    }
}A,B;
bool equal(int i,int j,int k){
    return A.cal(i,k)==A.cal(j,k)&&B.cal(i,k)==B.cal(j,k);
}
int main(){
    scanf("%d%s",&n,s+1);
    A.mod=998244353;B.mod=1000000007;
    A.work();B.work();
    for (int i=n>>1,j=0;i;--i,j=min(j+2,(n>>1)-i))
        if (equal(i,n,i))
            while (~j){
                if (!equal(i+j,n-i,j)) --j;
                else {ans=max(ans,i+j);break;}
            }
    printf("%d\n",ans);return 0;
}

[BZOJ3060][Poi2012]Tour de Byteotia

两端点都\(>k\)的边一定不会被删。

所以先加入所有两端点\(>k\)的边,再拿剩下的边做一次\(\mbox{Kruskal}\)状的东西即可。

#include<cstdio>
#include<algorithm>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 2e6+5;
int n,m,k,fa[N],a[N],b[N],ans;
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int main(){
    n=gi();m=gi();k=gi();
    for (int i=1;i<=n;++i) fa[i]=i;
    for (int i=1;i<=m;++i){
        a[i]=gi(),b[i]=gi();
        if (min(a[i],b[i])>k) fa[find(a[i])]=find(b[i]),++ans;
    }
    for (int i=1;i<=m;++i)
        if (find(a[i])^find(b[i]))
            fa[find(a[i])]=find(b[i]),++ans;
    printf("%d\n",m-ans);return 0;
}

转载于:https://www.cnblogs.com/zhoushuyu/p/9740491.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 2012-2021年期间,百度地图点击量激增,。该点亮了在POI数据方面取得了重大突破与丰硕成果。 首先,在数据广度方面,百度POI数据覆盖范围不断扩大。从城市范围到县级范围,再到乡镇、村庄的覆盖,百度POI数据涵盖了更多的地理位置信息,为用户提供了更准确、细致的地理位置标识。 其次,在数据深度方面,百度POI数据逐渐丰富与完善。从最初提供基本的商家、景点信息,到后来增加了酒店、餐厅、娱乐场所等更多类别的POI数据,百度POI数据的种类更加丰富多样,用户能够更方便地查找到所需的具体位置与服务。 此外,百度POI数据还不断更新与优化,保证了数据的时效性与准确性。2012-2021年期间,百度通过引入大量国内外的数据合作伙伴,对POI进行不断的更新与整理,提高了数据的准确性和质量。此外,百度还通过众包的方式,鼓励用户亲身体验并提供POI数据的反馈,加快了数据的更新速度。 至今,百度POI数据已经成为用户寻找位置、探索周边、选择服务的重要工具。它不仅为用户提供了丰富的信息资源,也促进了电商、O2O等领域的快速发展。相信在未来,百度POI数据将继续发挥重要的作用,为用户带来更好的地理位置服务体验。 ### 回答2: 2012-2021年间,百度POI数据经历了长足的发展和变化。百度POIPoint of Interest)是指百度地图中所标注的各种地点和位置信息,如餐馆、商店、景点等。 首先,百度POI数据在这十年间数量迅速增加。随着用户对位置信息需求的增长和百度地图用户群体的扩大,越来越多的POI数据被纳入百度地图数据库中。这使得用户可以更方便地查询、导航和了解周围的位置信息。 其次,百度POI数据的准确性和完整性也得到了提升。百度不断优化算法和增加数据源,以提高POI数据的可信度和实用性。例如,百度通过搜集用户的反馈信息和使用大数据分析技术,不断更新POI信息,保证了数据的最新性和准确性。 另外,百度还逐渐完善了POI数据的分类和检索方式。除了常见的餐馆、商店等基础分类,百度还增加了更具细分特色的POI数据,如咖啡馆、超市、健身房等。同时,借助人工智能和自然语言处理技术,百度也加强了对POI数据的语义理解和智能检索能力,提供了更优质的POI搜索结果。 最后,随着移动互联网的普及和技术的发展,百度还将POI数据与其他服务相结合,创造了更多有价值的功能。例如,百度将POI数据与打车服务相结合,提供了快速叫车的功能;将POI数据与外卖服务相结合,实现了一键订餐和送餐服务。 总之,2012-2021年间,百度POI数据经历了数量的大幅增加,准确性的提升,分类和检索方式的完善,以及与其他服务的结合创新。这些改进使得用户能够更方便地获取、利用和享受基于位置信息的服务。 ### 回答3: 2012年至2021年期间,百度POI(兴趣点)数据经历了长足的发展和变化。POI数据是指百度地图上所标注的各类地理位置信息,包括商业设施、交通设施、餐饮娱乐、医疗教育等。 在这十年间,随着移动互联网的普及和用户需求的增加,百度POI数据逐渐扩大和完善。首先,百度增加了大量新的POI数据点,覆盖了更多的城市和区域。不仅一线城市和旅游热点地区有了详细的POI信息,二线城市和乡村地区的POI数据也得到了增强。 其次,百度POI数据的分类和分类精度也得到了提高。原先的POI分类主要包括餐饮、购物、住宿等基本类别,而现在的POI数据能更加详细地划分为各种具体类型,如咖啡厅、超市、酒店等,方便用户查找和选择。 在数据更新方面,百度也加强了POI数据的更新频率和准确性。由于城市的发展变化以及商业设施的兴衰,百度通过不断收集用户反馈和行业动态,及时更新POI数据,确保用户能获得最新最准确的信息。 此外,随着技术的进步,2012年至2021年间,百度POI数据也逐渐与其他数据源进行整合。例如,百度结合了社交媒体数据、公共交通数据等,使得POI数据更具实时性和多样化。 总的来说,2012-2021年是百度POI数据不断发展和完善的十年。通过增加新的POI数据点、提高分类和分类精度、加强数据更新和与其他数据源整合,百度不断提升了POI数据的质量和覆盖面,为用户提供更好的地理位置信息服务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值