POI2011题解
POI2011题解
2214先咕一会。。。
[BZOJ2212][POI2011]Tree Rotations
线段树合并模板题。
#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 = 2e5+5;
struct seg{int ls,rs,sz;}t[N*80];
int n,tot;long long s1,s2,ans;
int build(int l,int r,int p){
int x=++tot,mid=l+r>>1;t[x].sz=1;
if (l==r) return x;
if (p<=mid) t[x].ls=build(l,mid,p);
else t[x].rs=build(mid+1,r,p);
return x;
}
int merge(int x,int y){
if (!x||!y) return x|y;
int z=++tot;t[z].sz=t[x].sz+t[y].sz;
s1+=1ll*t[t[x].ls].sz*t[t[y].rs].sz;
s2+=1ll*t[t[x].rs].sz*t[t[y].ls].sz;
t[z].ls=merge(t[x].ls,t[y].ls);
t[z].rs=merge(t[x].rs,t[y].rs);
return z;
}
int dfs(){
int x=gi();if (x) return build(1,n,x);
int ls=dfs(),rs=dfs();s1=s2=0;
x=merge(ls,rs);ans+=min(s1,s2);return x;
}
int main(){n=gi();dfs();printf("%lld\n",ans);return 0;}
[BZOJ2213][Poi2011]Difference
记\(s_{i,j}\)表示前\(i\)个位置里字符\(j\)的数量。我们相当于是要求\(\max\{(s_{r,a}-s_{l,a})-(s_{r,b}-s_{l,b})\}=\max\{(s_{r,a}-s_{r,b})-(s_{l,a}-s_{l,b})\}\),其中\(s_{r,b}-s_{l,b}\neq0\)。
所以只需要维护前缀\(s_{l,a}-s_{l,b}\)的最小值即可,每加一个字符至多只会使\(26\)个最小值发生改变,但因为\(s_{r,b}-s_{l,b}\neq0\)的这个烦人的限制,所以还需要记录一下更新最小值的位置。复杂度\(O(26n)\)。
最后还要\(O(26^2)\)判一遍。给组数据:\(s=\)'aab'
#include<cstdio>
#include<algorithm>
using namespace std;
int n,sum[26],las[26],p[26][26],mn[26][26],ans;char s[1000005];
int main(){
scanf("%d%s",&n,s+1);
for (int i=1;i<=n;++i){
int c=s[i]-'a';++sum[c];las[c]=i;
for (int j=0;j<26;++j)
if (sum[j])
ans=max(ans,sum[c]-sum[j]-mn[c][j]-(las[j]==p[c][j]));
for (int j=0;j<26;++j)
if (sum[j]-sum[c]<mn[j][c])
mn[j][c]=sum[j]-sum[c],p[j][c]=i;
}
for (int i=0;i<26;++i)
for (int j=0;j<26;++j)
if (sum[j])
ans=max(ans,sum[i]-sum[j]-mn[i][j]-(las[j]==p[i][j]));
printf("%d\n",ans);return 0;
}
[BZOJ2215][Poi2011]Conspiracy
先构造出一组合法方案。可以使用\(\mbox{2-sat}\),对于任两个点,如果他们之间有边则不能同时存在于独立集中,否则不能同时存在与团中。这样的复杂度是\(O(n^2)\)的。
然后考虑从当前方案出发生成其余的方案。可以发现,一个集合中不可能有超过一个点进入另一个集合,所以新方案生成只有三种方法:从独立集中丢一个点进团,从团中丢一个点进独立集,或者是双方交换一对点。
所以就维护一下每个点是不是只与对方集合中的某一个点冲突,然后统计答案即可。注意一下两个点集的数量。
#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 = 5005;
int n,a[N][N],to[N*N],nxt[N*N],head[N<<1],cnt,dfn[N<<1],low[N<<1],tim,vis[N<<1],S[N<<1],bel[N<<1],scc,col[N],ban[N],tot[2],ans;
void link(int u,int v){
to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
u^=1;v^=1;swap(u,v);
to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
}
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;
do x=S[S[0]--],vis[x]=0,bel[x]=scc;while (x^u);
}
}
int main(){
n=gi();
for (int i=1,j;i<=n;++i){
j=gi();while (j--) a[i][gi()]=1;
}
for (int i=1;i<=n;++i)
for (int j=i+1;j<=n;++j)
if (a[i][j]) link(i<<1|1,j<<1);
else link(i<<1,j<<1|1);
for (int i=2;i<=(n<<1|1);++i) if (!dfn[i]) Tarjan(i);
for (int i=1;i<=n;++i) if (bel[i<<1]==bel[i<<1|1]) return puts("0"),0;
for (int i=1;i<=n;++i) col[i]=bel[i<<1]<bel[i<<1|1],++tot[col[i]];
if (tot[0]&&tot[1]) ++ans;
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
if (col[i]!=col[j]&&a[i][j]!=col[j]){
if (!ban[i]) ban[i]=j;
else {ban[i]=-1;break;}
}
if (tot[0]>1)
for (int i=1;i<=n;++i)
if (col[i]==0&&!ban[i]) ++ans;
if (tot[1]>1)
for (int i=1;i<=n;++i)
if (col[i]==1&&!ban[i]) ++ans;
if (tot[0]&&tot[1])
for (int i=1;i<=n;++i)
for (int j=i+1;j<=n;++j)
if (col[i]!=col[j]&&(ban[i]==0||ban[i]==j)&&(ban[j]==0||ban[j]==i)) ++ans;
printf("%d\n",ans);return 0;
}
[BZOJ2216][Poi2011]Lightning Conductor
决策单调性模板题。
#include<cstdio>
#include<algorithm>
#include<cmath>
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,hd,tl;double a[N],dp1[N],dp2[N];
struct node{int p,l,r;}q[N];
double cal(int j,int i){
return a[j]+sqrt(i-j)-a[i];
}
int binary(int x,int y){
int l=q[tl].l,r=n,res=n;
while (l<=r){
int mid=l+r>>1;
if (cal(x,mid)>cal(y,mid)) res=mid,r=mid-1;
else l=mid+1;
}
return l;
}
void work(double *dp){
hd=1,tl=0;
for (int i=1;i<=n;++i){
++q[hd].l;
if (hd<=tl&&q[hd].l>q[hd].r) ++hd;
if (hd>tl||cal(i,n)>cal(q[tl].p,n)){
while (hd<=tl&&cal(i,q[tl].l)>cal(q[tl].p,q[tl].l)) --tl;
if (hd>tl) q[++tl]=(node){i,i,n};
else{
int x=binary(i,q[tl].p);
q[tl].r=x-1;q[++tl]=(node){i,x,n};
}
}
dp[i]=cal(q[hd].p,i);
}
}
int main(){
n=gi();
for (int i=1;i<=n;++i) a[i]=gi();
work(dp1);
reverse(a+1,a+n+1);
work(dp2);
for (int i=1;i<=n;++i) printf("%d\n",(int)ceil(max(dp1[i],dp2[n-i+1])));
return 0;
}
[BZOJ2217][Poi2011]Lollipopz
因为只有\(1\)或者\(2\),所以对这个序列求前缀和后,不能用前缀和表示的数一定不相邻。
或者说,如果一个数\(x\)不能被某个前缀和表示出来,那么\(x-1\)跟\(x+1\)都一定可以被表示出来。(边界情况除外)
如果询问的\(x\)可以被前缀和表示出来我们就直接用这个前缀,否则,找到\(x+1\)对应的前缀,把左右端点同时右移,直到左端点变成\(1\)或者右端点\(+1\)变成\(1\),就可以使这个区间和\(-1\)从而表示出\(x\)。
#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,sum[N],f[N<<1],len[N];char s[N];
int main(){
n=gi();m=gi();scanf("%s",s+1);
for (int i=1;i<=n;++i) sum[i]=sum[i-1]+(s[i]=='W'?1:2),f[sum[i]]=i;
for (int i=n;i;--i) if (s[i]=='T') len[i]=len[i+1]+1;
while (m--){
int x=gi();
if (x>sum[n]) puts("NIE");
else if (f[x]) printf("1 %d\n",f[x]);
else if (len[f[x+1]]>len[1]) printf("%d %d\n",2+len[1],f[x+1]+len[1]);
else if (f[x+1]+len[f[x+1]]<=n) printf("%d %d\n",1+len[f[x+1]],f[x+1]+len[f[x+1]]);
else puts("NIE");
}
return 0;
}
[BZOJ2276][Poi2011]Temperature
之前萝卜考试蒯的原题。在考场上\(YY\)了一个神奇的链表维护\(dp\)的做法。
大致就是说设\(f_i\)表示长度为\(i\)时最后一位的最小值。对于每个\([L,R]\),可以把前缀\(\le L\)的全部改成\(L\),再把后面大于\(R\)的删掉就行了。复杂度\(O(n)\) 。
#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,pre[N],nxt[N],val[N],sz[N],tot,hd,tl,len,ans;
int main(){
n=gi();
for (int i=1;i<=n;++i){
int L=gi(),R=gi(),S=1;
while (hd&&val[hd]<=L) S+=sz[hd],hd=nxt[hd],pre[hd]=0;
if (!hd) val[hd=tl=++tot]=L,sz[tot]=S;
else pre[hd]=++tot,nxt[tot]=hd,val[tot]=L,sz[tot]=S,hd=tot;
++len;
while (tl&&val[tl]>R) len-=sz[tl],tl=pre[tl],nxt[tl]=0;
ans=max(ans,len);
}
printf("%d\n",ans);return 0;
}
[BZOJ2277][Poi2011]Strongbox
首先密码的构成一定是\(0,x,2x,3x...\),其中\(x|n\)。题中要求最大化密码的数量,也就是最小化\(x\)。已知\(x|a_k,x\nmid a_i(1\le i < k)\)。那么\(\gcd(a_i,a_k)(1\le i<k)\)以及它们的因子都不能作为答案。把\(\gcd(n,a_k)\)的所有因子拿出来,根据其内部的拓扑关系递推一下即可。复杂度最低可以做到\(包含的质因子个数O(d(n)\times n\mbox{包含的质因子个数})\) 。
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
ll gi(){
ll 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;
ll n,a[N],d[N],p[N];
int k,tot,cnt,ban[N];
int main(){
n=gi();k=gi();
for (int i=1;i<=k;++i) a[i]=gi();
a[k]=__gcd(a[k],n);
for (ll i=1;i*i<=a[k];++i)
if (a[k]%i==0){
d[++tot]=i;
if (i*i<a[k]) d[++tot]=a[k]/i;
}
sort(d+1,d+tot+1);ll tmp=a[k];
for (ll i=2;i*i<=tmp;++i)
if (tmp%i==0){
p[++cnt]=i;
while (tmp%i==0) tmp/=i;
}
if (tmp>1) p[++cnt]=tmp;
for (int i=1;i<k;++i)
ban[lower_bound(d+1,d+tot+1,__gcd(a[i],a[k]))-d]=1;
for (int i=tot;i;--i)
if (ban[i])
for (int j=1;j<=cnt;++j)
if (d[i]%p[j]==0)
ban[lower_bound(d+1,d+tot+1,d[i]/p[j])-d]=1;
for (int i=1;i<=tot;++i)
if (!ban[i]) return printf("%lld\n",n/d[i]),0;
}
[BZOJ2278][Poi2011]Garbage
一定存在一种方案使得所有的简单环不相交。所以那些不需要改变状态的边是没有用的。
直接\(dfs\),搜到一个环就把它拿出来删掉。注意要加上当前弧优化不然要\(TLE\)。
#include<cstdio>
#include<algorithm>
#include<vector>
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 = 1e5+5;
const int M = 2e6+5;
int n,m,to[M],nxt[M],head[N],cnt=1,du[N],S[N],ins[N],vis[M],tot;
vector<int>ans[M];
void dfs(int u){
if (ins[u]){
++tot;int x=0;
do x=S[S[0]--],ins[x]=0,ans[tot].push_back(x);while (x^u);
}
for (int &e=head[u];e;e=nxt[e])
if (!vis[e]){
vis[e]=vis[e^1]=1;
ins[S[++S[0]]=u]=1;
dfs(to[e]);
}
}
int main(){
n=gi();m=gi();
for (int i=1;i<=m;++i){
int u=gi(),v=gi();
if (gi()^gi()){
to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
to[++cnt]=u;nxt[cnt]=head[v];head[v]=cnt;
++du[u];++du[v];
}
}
for (int i=1;i<=n;++i) if (du[i]&1) return puts("NIE"),0;
for (int i=1;i<=n;++i) dfs(i);
printf("%d\n",tot);
for (int i=1;i<=tot;++i){
int sz=ans[i].size();printf("%d ",sz);
for (int j=0;j<sz;++j) printf("%d ",ans[i][j]);
printf("%d\n",ans[i][0]);
}
return 0;
}
[BZOJ2280][Poi2011]Plot
二分答案,然后做一个最小圆覆盖。该算法复杂度在期望意义下是\(O(n)\),证明略。
做圆覆盖的时候需要再次二分最大的合法右端点,但是直接二分可能导致复杂度退化,可以先倍增求出最大的\(k\)使得长为\(2^{k-1}\)的区间合法而长为\(2^k\)的区间不合法,再在\([2^{k-1},2^k)\)里二分右端点。
对着Claris代码调的,写得可能有点凌乱哈。
#include<cstdio>
#include<algorithm>
#include<ctime>
#include<cmath>
using namespace std;
const int N = 1e5+5;
const double eps = 1e-10;
struct node{double x,y;}a[N],b[N],O;
int n,m,len,tim=50,tot,ans[2][N];double R;
double dist(node a,node b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
void work(node A,node B,node C){
double a,b,c,d,e,f;
a=A.x-C.x;b=A.y-C.y;
c=B.x-C.x;d=B.y-C.y;
e=A.x*A.x+A.y*A.y-C.x*C.x-C.y*C.y;
f=B.x*B.x+B.y*B.y-C.x*C.x-C.y*C.y;
O.x=(d*e-b*f)/(2*a*d-2*b*c);
O.y=(a*f-c*e)/(2*a*d-2*b*c);
R=dist(O,A);
}
void calc(int l,int r){
len=0;
for (int i=l;i<=r;++i) b[++len]=a[i];
for (int i=1;i<=len;++i) swap(b[i],b[1+rand()%len]);
O=b[1];R=0;
for (int i=1;i<=len;++i)
if (dist(O,b[i])>R+eps){
O=b[i];R=0;
for (int j=1;j<i;++j)
if (dist(O,b[j])>R+eps){
O.x=(b[i].x+b[j].x)/2;
O.y=(b[i].y+b[j].y)/2;
R=dist(b[i],O);
for (int k=1;k<j;++k)
if (dist(O,b[k])>R+eps)
work(b[i],b[j],b[k]);
}
}
}
bool check(double lim){
tot=0;
for (int i=1,j,t,l,r;i<=n;i=j+1){
for (t=1;i+(1<<t)-1<=n;++t){
calc(i,i+(1<<t)-1);
if (R>lim+eps) break;
}
j=i,l=i+(1<<t-1)-1,r=min(n,i+(1<<t)-1);
while (l<=r){
int mid=l+r>>1;calc(i,mid);
if (R<lim+eps) j=mid,l=mid+1;
else r=mid-1;
}
ans[0][++tot]=i;ans[1][tot]=j;
if (tot>m) return false;
}
return true;
}
int main(){
scanf("%d%d",&n,&m);srand(20020415);
for (int i=1;i<=n;++i) scanf("%lf%lf",&a[i].x,&a[i].y);
calc(1,n);double l=0,r=R;
if (m>1)
while (tim--&&r-l>eps){
double mid=(l+r)/2;
if (check(mid)) r=mid;else l=mid;
}
check(r);printf("%.8f\n%d\n",r,tot);
for (int i=1;i<=tot;++i) calc(ans[0][i],ans[1][i]),printf("%.8f %.8f\n",O.x,O.y);
return 0;
}
[BZOJ2525][Poi2011]Dynamite
二分答案,然后贪心覆盖就可以了。对每个点维护\(f_i\)表示子树中最近的已覆盖点到\(i\)的距离,\(g_i\)表示子树中最远的未覆盖点到\(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 = 3e5+5;
const int inf = 1<<30;
int n,m,mrk[N],to[N<<1],nxt[N<<1],head[N],cnt,k,tot,f[N],g[N];
void dfs(int u,int fa){
g[u]=mrk[u]?0:-inf;f[u]=inf;
for (int e=head[u];e;e=nxt[e])
if (to[e]!=fa){
int v=to[e];dfs(v,u);
if (g[u]+f[v]+1<=k) g[u]=-inf;
if (f[u]+g[v]+1>k) g[u]=max(g[u],g[v]+1);
f[u]=min(f[u],f[v]+1);
}
if (g[u]==k) f[u]=0,++tot;
if (f[u]+g[u]<=k) g[u]=-inf;
}
bool check(int mid){
k=mid;tot=0;dfs(1,0);
if (f[1]+g[1]>k) ++tot;
return tot<=m;
}
int main(){
n=gi();m=gi();
for (int i=1;i<=n;++i) mrk[i]=gi();
for (int i=1;i<n;++i){
int u=gi(),v=gi();
to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
to[++cnt]=u;nxt[cnt]=head[v];head[v]=cnt;
}
int l=0,r=n-1,res=0;
while (l<=r){
int mid=l+r>>1;
if (check(mid)) res=mid,r=mid-1;
else l=mid+1;
}
printf("%d\n",res);return 0;
}
[BZOJ2526][Poi2011]Inspection
如果一个点要有答案那就需要满足最大子树的大小不超过\(\frac n2\),而显然这性质只有重心才满足。所以实际上只有至多两个位置有答案其余的都是\(-1\)。
答案显然就是所有点到重心的距离减去最深的深度。注意如果有两个重心,那么当我们处理一个重心的时候,另一个重心所在的子树会比剩下的子树加起来还要大,这时候最深深度就必须在另一个重心所在的子树里面选。
#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,to[N<<1],nxt[N<<1],head[N],cnt,sz[N],w[N],f[N];
long long ans;
void dfs1(int u,int fa){
sz[u]=1;
for (int e=head[u];e;e=nxt[e])
if (to[e]!=fa){
dfs1(to[e],u),sz[u]+=sz[to[e]];
w[u]=max(w[u],sz[to[e]]);
}
w[u]=max(w[u],n-sz[u]);
}
void dfs2(int u,int fa){
sz[u]=1;f[u]=0;
for (int e=head[u];e;e=nxt[e])
if (to[e]!=fa){
dfs2(to[e],u),sz[u]+=sz[to[e]];
f[u]=max(f[u],f[to[e]]+1);
ans+=sz[to[e]];
}
}
int main(){
n=gi();
for (int i=1;i<n;++i){
int u=gi(),v=gi();
to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
to[++cnt]=u;nxt[cnt]=head[v];head[v]=cnt;
}
dfs1(1,0);
for (int i=1;i<=n;++i)
if (w[i]<=(n>>1)){
ans=0;dfs2(i,0);ans<<=1;
if ((~n&1)&&w[i]==(n>>1)){
for (int e=head[i];e;e=nxt[e])
if (sz[to[e]]==(n>>1)) {ans-=f[to[e]]+1;break;}
}else ans-=f[i];
printf("%lld\n",ans);
}else puts("-1");
return 0;
}
[BZOJ2527][Poi2011]Meteors
整体二分模板题。
BZ的神奇数据居然可以卡爆\(\mbox{long long}\)。。。然后我开个\(\mbox{unsigned long long}\)就过了?讲道理这里\(\mbox{unsigned long long}\)也能爆吧。。。
#include<cstdio>
#include<algorithm>
#include<vector>
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 unsigned long long
const int N = 300005;
int n,m,k,p[N],st[N],ed[N],ans[N];
struct nation{ll p;int id;}q[N],q1[N],q2[N];
vector<int>a[N];ll val[N],c[N];
void mdf(int k,ll v){while(k<=m)c[k]+=v,k+=k&-k;}
ll qry(int k){ll s=0;while(k)s+=c[k],k^=k&-k;return s;}
void upt(int p,ll f){
if (st[p]<=ed[p])
mdf(st[p],val[p]*f),mdf(ed[p]+1,-val[p]*f);
else
mdf(1,val[p]*f),mdf(ed[p]+1,-val[p]*f),mdf(st[p],val[p]*f);
}
void solve(int L,int R,int l,int r){
if (L>R) return;
if (l==r){
for (int i=L;i<=R;++i) ans[q[i].id]=l;
return;
}
int mid=l+r>>1,t1=0,t2=0;
for (int i=l;i<=mid;++i) upt(i,1);
for (int i=L;i<=R;++i){
ll tmp=0;
for (int j=0,sz=a[q[i].id].size();j<sz;++j)
tmp+=qry(a[q[i].id][j]);
if (tmp>=q[i].p) q1[++t1]=q[i];
else q[i].p-=tmp,q2[++t2]=q[i];
}
for (int i=l;i<=mid;++i) upt(i,-1);
for (int i=L,j=1;j<=t1;++i,++j) q[i]=q1[j];
for (int i=L+t1,j=1;j<=t2;++i,++j) q[i]=q2[j];
solve(L,L+t1-1,l,mid);solve(L+t1,R,mid+1,r);
}
int main(){
n=gi();m=gi();
for (int i=1;i<=m;++i) a[gi()].push_back(i);
for (int i=1;i<=n;++i) q[i]=(nation){gi(),i};
k=gi();
for (int i=1;i<=k;++i) st[i]=gi(),ed[i]=gi(),val[i]=gi();
++k;st[k]=1;ed[k]=m;val[k]=1<<30;
solve(1,n,1,k);
for (int i=1;i<=n;++i)
if (ans[i]==k) puts("NIE");
else printf("%d\n",ans[i]);
return 0;
}
[BZOJ2529][Poi2011]Sticks
拼成三角形的三根木棍一定是按长度排序后相邻的(不考虑颜色的话),所以就从前往后维护三根木棍就好了。
#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 fi first
#define se second
const int N = 1e6+5;
int k,n;pair<int,int>a[N],b[3];
int main(){
k=gi();
for (int i=1;i<=k;++i){
int m=gi();
while (m--) a[++n]=make_pair(gi(),i);
}
sort(a+1,a+n+1);
for (int i=1;i<=n;++i){
if (a[i].se==b[0].se) b[0]=a[i];
else if (a[i].se==b[1].se) b[1]=a[i];
else if (a[i].se==b[2].se) b[2]=a[i];
else b[0]=a[i];
sort(b,b+3);
if (b[0].se&&b[0].fi+b[1].fi>b[2].fi)
return printf("%d %d %d %d %d %d\n",b[0].se,b[0].fi,b[1].se,b[1].fi,b[2].se,b[2].fi),0;
}
puts("NIE");return 0;
}
[BZOJ2530][Poi2011]Party
随便选两个未被删掉的点,如果两个点之间没有边,那么就把这两个点都删掉。
因为团的大小有\(\frac 23n\),而删掉的两个点不可能都在团里,所以至少删掉了一个需要删的,那么肯定可以剩下一个大小为\(\frac 13n\)的团。
#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 = 3005;
int n,m,k,a[N][N],del[N];
int main(){
n=gi();m=gi();k=n/3;
for (int i=1,u,v;i<=m;++i)
u=gi(),v=gi(),a[u][v]=a[v][u]=1;
for (int i=1;i<=n&&k;++i)
for (int j=i+1;j<=n&&k;++j)
if (!del[i]&&!del[j]&&!a[i][j])
--k,del[i]=del[j]=1;
k=0;
for (int i=1;i<=n;++i)
if (!del[i]){
printf("%d ",i);
if (++k==n/3) break;
}
puts("");return 0;
}
[BZOJ2557][Poi2011]Programming Contest
直观的想法是直接跑费用流,但是会\(TLE\)。
可以跑匈牙利,按罚时的先后顺序增广每一个点,匈牙利可以保证之前的匹配点不会被改变,所以这样跑是正确的。
注意增广一次的最坏复杂度是\(O(k)\)也就是\(O(nm)\)的,如果对每条边都增广一次复杂度就变成\(O(n^2m^2)\)了。可以对每个点标记一下是否增广失败过,然后如果之前就已经增广失败了这次就没必要再跑了。这样最多成功增广\(m\)次,每个点最多增广失败一次,复杂度就是\(O((n+m)nm)\)。
#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 = 505;
int n,m,r,t,k,cnt,to[N*N],nxt[N*N],head[N],d[N],mat[N],vis[N],tim,ban[N],ans1,ans2;
pair<int,int>a[N*N];
bool dfs(int u){
for (int e=head[u];e;e=nxt[e]){
int v=to[e];
if (vis[v]==tim) continue;vis[v]=tim;
if (!mat[v]||dfs(mat[v])) return mat[v]=u,true;
}
return false;
}
int main(){
n=gi();m=gi();r=gi();t=gi();k=gi();
for (int i=1;i<=k;++i){
int u=gi(),v=gi();
to[i]=v;nxt[i]=head[u];head[u]=i;
if (d[u]<t/r) a[++cnt]=make_pair(++d[u],u);
}
sort(a+1,a+cnt+1);
for (tim=1;tim<=cnt;++tim)
if (!ban[a[tim].second]&&dfs(a[tim].second))
++ans1,ans2+=a[tim].first;
else ban[a[tim].second]=1;
printf("%d %lld\n",ans1,1ll*ans2*r);
return 0;
}
POI2011题解的更多相关文章
- BZOJ2527 & 洛谷3527:[Poi2011]Meteors——题解
+++++++++++++++++++++++++++++++++++++++++++ +本文作者:luyouqi233. + +欢迎访问我的博客:http://www.cnblogs.com/luy ...
- [Poi2011]Meteors 题解
题目大意: 给定一个环,每个节点有一个所属国家,k次事件,每次对[l,r]区间上的每个点点权加上一个值,求每个国家最早多少次操作之后所有点的点权和能达到一个值. 思路: 整体二分(二分答案),对于每个 ...
- 题解 P3521 【[POI2011]ROT-Tree Rotations】
这道题采用权值线段树合并的解法. 首先讲一下解法中出现的两个概念:权值线段树与线段树合并. 所谓权值线段树,可以理解为维护的信息反过来的普通线段树,我个人认为值域线段树这个名字其实要准确一些. 举个例 ...
- 洛谷 P3518 [POI2011] SEJ-Strongbox 题解
思路: 首先先将每个输入的数据与n的最大公约数求出(因为如果a[i]是密码,那么所有a[i]与n最大公约数的倍数也是密码:于是如果a[i]不是密码,那么所有a[i]与n最大公约数的倍数也都不是密码)再 ...
- [POI2011]ROT-Tree Rotations 题解
题面 这道题咋看都是无法从dp入手,那么就从数据结构入手!: 首先你要会权值线段树和线段树合并. 然后你要知道: 对于任意一个节点,交换左右子树对当前节点和前面的所有节点没有影响. 因为这是前序遍历: ...
- [POI2011]SMI-Garbage 题解
题面 想必各位大佬一定想到了把现在和目标值不一致的边加入到一个新建的图上: 问题就变为了在新的图上寻找有多少个欧拉回路,并输出这些路径: 我们可以用栈来记录情况,然后对于会回答稍微处理处理就好了: # ...
- 题解 洛谷 P3521 【[POI2011]ROT-Tree Rotations】
给定一棵二叉树,叶子节点有权值,可以进行若干次交换一个节点的左右儿子的操作,使前序遍历叶子的逆序对最少. 考虑一个节点下子树逆序对的产生: ① 只在左子树中产生. ② 只在右子树中产生. ③ 在左子树 ...
- BZOJ2527: [Poi2011]Meteors
补一发题解.. 整体二分这个东西,一开始感觉复杂度不是很靠谱的样子 问了po姐姐,说套主定理硬干.. #include<bits/stdc++.h> #define ll long lon ...
- BZOJ2276: [Poi2011]Temperature
2276: [Poi2011]Temperature Time Limit: 20 Sec Memory Limit: 32 MBSubmit: 293 Solved: 117[Submit][S ...
随机推荐
- mysql数据库权限
use mysql select * from user \G; UPDATE user set password=PASSWORD('root') where user='root' grant a ...
- ng-深度学习-课程笔记-16: 自然语言处理与词嵌入(Week2)
1 词汇表征(Word representation) 用one-hot表示单词的一个缺点就是它把每个词孤立起来,这使得算法对词语的相关性泛化不强. 可以使用词嵌入(word embedding)来解 ...
- iOS App迁移(App Transfer)注意点
1.App迁移需要苹果审核吗? 答:不需要 2.App迁移需要多长时间? 答:迁移操作过程很快,A账号发出申请,B账号接收,几分钟时间.App Store 展示B账号相关信息可能几分钟,也可能有延迟几 ...
- bzoj1626 / P2872 [USACO07DEC]道路建设Building Roads
P2872 [USACO07DEC]道路建设Building Roads kruskal求最小生成树. #include<iostream> #include<cstdio> ...
- bzoj1618 / P2918 [USACO08NOV]买干草Buying Hay(完全背包)
P2918 [USACO08NOV]买干草Buying Hay 显然的完全背包 设$f[i]$为买$i$磅干草的最小代价 搞搞完全背包即可 注意到最后可能买的干草超出范围,但是价格可能更低. 于是我们 ...
- java读取pdf总结
第三方软件 1.pdfbox PDFBox 0.7.3.PDFBox是一个开源的对pdf文件进行操作的库. PDFBox-0.7.3.jar加入classpath.同时FontBox1.0.jar加入 ...
- UVa 1658 海军上将(最小费用最大流)
https://vjudge.net/problem/UVA-1658 题意: 给出一个v个点e条边的有向加权图,求1~v的两条不相交(除了起点和终点外公共点)的路径,使得权和最小. 思路:把2到v- ...
- class []的用法
span[class='test'] =>匹配所有带有class类名test的span标签 span[class *='test'] =>匹配所有包含了test字符串的class类 ...
- append 注意事项
>>> t1 = [, ] >>> t2 = t1.append() >>> t1 [, , ] >>> t2 None
- PHP通过日志来发现问题
三大日志 1.Nginx的错误日志, Nginx的Access日志 2.PHPfpm的错误日志可设置级别 3.Mysql的慢查询日志