POI2013题解
POI2013题解
只做了BZ上有的\(13\)道题。
就这样还扔了两道神仙构造和一道计算几何题。所以只剩下十道题了。
[BZOJ3414][Poi2013]Inspector
肯定是先二分答案,然后每个人的出现区间至少要包含于他自己记录的所有时间点。如果某个人没有记录过那他的出现区间任意。
从左往右扫描,维护以下几个东西:
\(t\):目前还有多少人的区间不确定。
\(s\):当前要求选多少人。(这个是由记录者决定的)
\(cl\):有多少人的区间可以向左扩展。
\(cr\):有多少人的区间可以向右扩展。
每扫到一个位置,如果\(s>\)记录的人数则无解,否则\(s+cl+cr\)就是当前可存在于该位置上的人数,若人多了就优先减\(cr\)再减\(cl\),若人少了就从\(t\)中拿人补到\(cl\)中。
#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 = 1e5+5;
int n,m,tim[N],peo[N],rec[N],L[N],R[N],num[N],pl[N],pr[N];
bool check(int mid){
for (int i=1;i<=n;++i) L[i]=N,R[i]=0;
for (int i=1;i<=m;++i) num[i]=pl[i]=pr[i]=0;
for (int i=1;i<=mid;++i){
int t=tim[i],p=peo[i],r=rec[i];
if (num[t]&&num[t]!=r) return false;
num[t]=r;L[p]=min(L[p],t);R[p]=max(R[p],t);
}
for (int i=1;i<=n;++i) if (R[i]) ++pl[L[i]],++pr[R[i]];
for (int i=1,t=n,s=0,cl=0,cr=0;i<=m;++i)
if (num[i]){
s+=pl[i];
while (pl[i]--) cl?--cl:--t;
if (s>num[i]) return false;
while (s+cl+cr<num[i]) ++cl,--t;
while (s+cl+cr>num[i]) cr?--cr:--cl;
s-=pr[i];cr+=pr[i];
if (t<0) return false;
}
return true;
}
int main(){
int Case=gi();while (Case--){
n=gi();m=gi();
for (int i=1;i<=m;++i) tim[i]=gi(),peo[i]=gi(),rec[i]=gi()+1;
int l=0,r=m,res=0;
while (l<=r){
int mid=l+r>>1;
if (check(mid)) res=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",res);
}
return 0;
}
[BZOJ3415][Poi2013]Price List
可知有且仅有三种策略:
1、只走\(a\)边,可以当做\(b\)边不存在。
2、把两条\(a\)边并做一条\(b\)边走。
3、一条\(a\)边都不会走。
前两种直接在原图中\(bfs\)即可出解。
考虑第三种,如果在\(bfs\)的过程中对于每个点\(x\)枚举所有与他距离为\(2\)的点,复杂度会退化成\(O(m^2)\)。
假设我们有两个边集\(G_0,G_1\),初始时都为原图。从队列中取出一个点\(u\),先枚举其在\(G_0\)中的所有出边指向的点\(v\)并标记,再顺次枚举每个\(v\)在\(G_1\)中的出边指向的点\(w\),如果\(w\)未被标记(\(u,v,w\)三点不构成三元环)则可用\(u\)更新\(w\),更新后把\(G_1\)中\(v\to w\)的这条边删除。
在不考虑三元环的前提下,\(G_0\)每条边被访问一起,\(G_1\)每条边也被访问一次,所以复杂度线性。但是构成三元环会导致边\(v\to w\)不能删。而三元环的数量是\(O(m\sqrt m)\)的,每个三元环会给复杂度带来一个常数(会被访问\(3\)次),所以复杂度为\(O(m\sqrt m)\)。
#include<cstdio>
#include<algorithm>
#include<cstring>
#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 = 2e5+5;
struct Graph{
int to[N],nxt[N],hd[N],cnt;
void link(int u,int v){
to[++cnt]=v;nxt[cnt]=hd[u];hd[u]=cnt;
to[++cnt]=u;nxt[cnt]=hd[v];hd[v]=cnt;
}
}G,F;
int n,m,S,A,B,dep[N],vis[N],ans[N];queue<int>Q;
int main(){
n=gi();m=gi();S=gi();A=gi();B=gi();
for (int i=1;i<=m;++i){
int u=gi(),v=gi();
G.link(u,v);F.link(u,v);
}
dep[S]=1;Q.push(S);
while (!Q.empty()){
int u=Q.front();Q.pop();
for (int e=G.hd[u];e;e=G.nxt[e]){
int v=G.to[e];
if (!dep[v]) dep[v]=dep[u]+1,Q.push(v);
}
}
for (int i=1;i<=n;++i) --dep[i],ans[i]=min(dep[i]*A,(dep[i]>>1)*B+(dep[i]&1)*A);
memset(dep,0,sizeof(dep));dep[S]=1;Q.push(S);
while (!Q.empty()){
int u=Q.front();Q.pop();
for (int e=G.hd[u];e;e=G.nxt[e]) vis[G.to[e]]=u;
for (int e=G.hd[u];e;e=G.nxt[e]){
int v=G.to[e];
for (int i=F.hd[v],las=0;i;i=F.nxt[i]){
int w=F.to[i];
if (vis[w]==u) {las=i;continue;}
if (!dep[w]) dep[w]=dep[u]+1,Q.push(w);
if (!las) F.hd[v]=F.nxt[F.hd[v]];
else F.nxt[las]=F.nxt[i];
}
}
}
for (int i=1;i<=n;++i) if (dep[i]) --dep[i],ans[i]=min(ans[i],dep[i]*B);
for (int i=1;i<=n;++i) printf("%d\n",ans[i]);
return 0;
}
[BZOJ3416][Poi2013]Take-out
从前往后依次压进栈中,如果栈内的前\(k+1\)个元素中恰好有一个白的就把这\(k+1\)个作为一组方案。倒序输出即可。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e6+5;
int n,k,q[N],sum[N],top,ans[N],cnt;char s[N];
int main(){
scanf("%d%d%s",&n,&k,s+1);++k;
for (int i=1;i<=n;++i){
q[++top]=i;sum[top]=sum[top-1]+(s[i]=='c');
if (top>=k&&sum[top]-sum[top-k]==1)
for (int tt=top-k;top>tt;--top)
ans[++cnt]=q[top];
}
for (int i=n;i;--i) printf("%d%c",ans[i],i%k==1?'\n':' ');
return 0;
}
[BZOJ3417][Poi2013]Tales of seafaring
\(O(n^2)\)预处理出\(i\)到\(j\)走奇数/偶数步的最短路。需要特判孤立点走大于零的偶数步到达自身是不合法的。
有人说预处理存不下?最短路长度最多\(2n\)你开个\(\mbox{short}\)存不就好啦?
#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 = 5005;
short n,m,to[N<<1],nxt[N<<1],head[N],cnt,q[N<<1],hd,tl,dep[N][N<<1];
int k;
int main(){
n=gi();m=gi();k=gi();
for (short i=1;i<=m;++i){
short u=gi(),v=gi();
to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
to[++cnt]=u;nxt[cnt]=head[v];head[v]=cnt;
}
memset(dep,-1,sizeof(dep));
for (short i=1;i<=n;++i){
dep[i][i<<1]=0;q[hd=tl=1]=i<<1;
while (hd<=tl){
short x=q[hd++],u=x>>1,c=(x&1)^1;
for (short e=head[u];e;e=nxt[e])
if (!~dep[i][to[e]<<1|c])
dep[i][to[e]<<1|c]=dep[i][x]+1,q[++tl]=to[e]<<1|c;
}
}
while (k--){
short u=gi(),v=gi();int d=gi();
if (u==v&&!head[u]&&d>0) puts("NIE");
else if (dep[u][v<<1|(d&1)]!=-1&&(int)dep[u][v<<1|(d&1)]<=d) puts("TAK");
else puts("NIE");
}
return 0;
}
[BZOJ3419][Poi2013]Taxis
首先可以发现的是,从大到小叫车是最优的,而且如果已行走距离超过了\(d\)就只需要一辆车就可以到终点了。
先从大到小叫车,这样如果有解的话一定是最优解,但是可能会无解,就比如说把所有\(\ge m-d\)的车全耗在了前半段导致后半段过不去。还是贪心,选出\(\ge m-d\)的最小的留给后半段,剩下的还是贪心选。这样就能得到答案了。
#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 = 5e5+5;
int n,p,ans;ll m,d,a[N],now;
bool cmp(ll i,ll j){return i>j;}
void cal(ll x){if(now<d)now+=max(0ll,x-d+now);else now=max(now,d+x);}
int main(){
m=gi();d=gi();n=gi();
for (int i=1;i<=n;++i) a[i]=gi();
sort(a+1,a+n+1,cmp);
for (int i=1;i<=n;++i){
cal(a[i]);if (now>=m) {ans=i;break;}
if (a[i]>=m-d) p=i;
}
if (!ans){
now=0;ll pos=d-(a[p]-m+d>>1);
for (int i=1;i<=n;++i)
if (i^p){
cal(a[i]);if (now>=pos) {ans=i;break;}
}
}
printf("%d\n",ans);return 0;
}
[BZOJ3420][Poi2013]Triumphal arch
先二分答案,设\(f_i\)表示在\(i\)节点至少需要预先在\(i\)子树内标记多少个点,转移\(f_i=\max\{0,\sum_j (f_j+1)-k\}\)。
#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 = 6e5+5;
int n,to[N],nxt[N],head[N],cnt;
int dfs(int u,int f,int k){
int s=0;
for (int e=head[u];e;e=nxt[e])
if (to[e]!=f) s+=dfs(to[e],u,k)+1;
return max(0,s-k);
}
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;
}
int l=0,r=n-1,res=0;
while (l<=r){
int mid=l+r>>1;
if (!dfs(1,0,mid)) res=mid,r=mid-1;
else l=mid+1;
}
printf("%d\n",res);return 0;
}
[BZOJ3421][Poi2013]Walk
有一个结论:在一个\(n\)维超立方体上有\(2^n\)个点,如果把这\(2^n\)个点分成\(2\)个点集,那么这样个点集之间的边数至少为两个点集的点数较小值。根据这个假设是已知的结论就可以证明一个定理:删除\(k\)个点后,图中存在至多一个大小大于\(nk\)的连通块,太懒了就不在这里证了。
所以直接\(bfs\),从\(S\)出发只扩展至多\(nk\)个状态,如果两边搜出了超过\(nk\)个状态还没有相遇就说明两点都在那个大于\(nk\)的连通块里面。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
const int N = 5e6+500;
const int mod = 5170427;
int n,k,nk,hd,tl,nxt[N],head[mod],cnt;ll a[N>>2],q[N],to[N];
char s[100];
ll read(){
scanf("%s",s);ll res=0;
for (int i=0;i<n;++i) res=res<<1|s[i]-'0';
return res;
}
void add1(ll x){
to[++cnt]=x;nxt[cnt]=head[x%mod];head[x%mod]=cnt;
}
void add2(ll x){
for (int e=head[x%mod];e;e=nxt[e])
if (to[e]==x) return;
q[++tl]=to[++cnt]=x;nxt[cnt]=head[x%mod];head[x%mod]=cnt;
}
void work(ll S,ll T){
memset(head,0,sizeof(head));cnt=0;
hd=1;tl=0;add2(S);
for (int i=1;i<=k;++i) add1(a[i]);
while (hd<=tl&&tl<=nk){
ll u=q[hd++];if (u==T) puts("TAK"),exit(0);
for (int i=0;i<n;++i) add2(u^(1ll<<i));
}
if (tl<=nk) puts("NIE"),exit(0);
}
int main(){
scanf("%d%d",&n,&k);nk=n*k;
ll S=read(),T=read();
for (int i=1;i<=k;++i) a[i]=read();
work(S,T);work(T,S);
puts("TAK");return 0;
}
[BZOJ3425][Poi2013]Polarization
最小答案一定是\(n-1\),只要按边深度的奇偶性取不同的方向就可以取到了。
最大答案存在一个结论,就是以重心为根,剩下每棵子树要么全部指向根,要么全部背离根。假设指向根的子树大小之和是\(x\),那么背离根的子树大小之和就是\(n-x-1\),所有穿过根的贡献就是\(x(n-x-1)\)。
所以现在要做的就是拿重心的所有儿子出来做一个\(01\)背包看可以组成哪些\(x\)。直接背包压位的话复杂度是\(O(\frac{n^2}{32})\),然后把\(\ge \sqrt n\)的暴力做,\(< \sqrt n\)的放在一起二进制分组做,复杂度就是\(O(\frac{n\sqrt n}{32})\)。
#include<cstdio>
#include<algorithm>
#include<bitset>
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 = 250005;
int n,to[N<<1],nxt[N<<1],head[N],cnt,sz[N],w[N],rt,s[505];
ll f[N];bitset<N>g;
void dfs(int u,int fa){
sz[u]=1;w[u]=f[u]=0;
for (int e=head[u],v;e;e=nxt[e])
if ((v=to[e])!=fa){
dfs(v,u);sz[u]+=sz[v];
f[u]+=f[v]+sz[v];w[u]=max(w[u],sz[v]);
}
w[u]=max(w[u],n-sz[u]);if (w[u]<w[rt]) rt=u;
}
int main(){
n=gi();if (n==1) return puts("0 0"),0;
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;
}
w[0]=n;dfs(1,0);dfs(rt,0);g[0]=1;
for (int e=head[rt];e;e=nxt[e])
if (sz[to[e]]>=500) g|=g<<sz[to[e]];
else ++s[sz[to[e]]];
for (int i=1;i<500;++i)
for (int j=s[i],k=1;j;j-=k,k=min(j,k<<1))
g|=g<<i*k;
for (int i=n>>1;i;--i)
if (g[i]) return printf("%d %lld\n",n-1,f[rt]+1ll*i*(n-i-1)),0;
}
[BZOJ3426][Poi2013]Tower Defense Game
直接每次选一个没覆盖的点就行了,因为这样做一定可以包含至少一个最优方案中的选择点。
至于维护这个覆盖,对每个点记一下最近的选择点到他的距离(代码中有点不一样,没关系啦),覆盖时如果不能更新这个距离就不走了(因为之前肯定已经走过了)。考虑到每个点的这个距离只会被更新常数次,所以复杂度是对的。
#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,to[N],nxt[N],head[N],cnt,vis[N],q[N],ans;
void dfs(int u,int c){
vis[u]=c;
for (int e=head[u];e;e=nxt[e])
if (vis[to[e]]<c-1) dfs(to[e],c-1);
}
int main(){
n=gi();m=gi();gi();
for (int i=1;i<=m;++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;
}
for (int i=1;i<=n;++i) if (!vis[i]) q[++ans]=i,dfs(i,3);
printf("%d\n",ans);
for (int i=1;i<=ans;++i) printf("%d ",q[i]);
puts("");return 0;
}
[BZOJ3427][Poi2013]Bytecomputer
猜个结论最终序列一定是一段\(-1\)一段\(0\)一段\(1\)?
这样以来就直接\(dp\)一下就好了。
#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,f[N][3];
int main(){
memset(f,63,sizeof(f));
n=gi();f[1][gi()+1]=0;
for (int i=2;i<=n;++i){
int x=gi();
if (x==1){
f[i][2]=min(f[i-1][2],min(f[i-1][0],f[i-1][1]));
f[i][1]=f[i-1][0]+1;f[i][0]=f[i-1][0]+2;
}else if (x==0){
f[i][1]=min(f[i-1][1],f[i-1][0]);
f[i][2]=f[i-1][2]+1;f[i][0]=f[i-1][0]+1;
}else f[i][0]=f[i-1][0],f[i][2]=f[i-1][2]+2;
}
int ans=min(f[n][0],min(f[n][1],f[n][2]));
if (ans==f[0][0]) puts("BRAK");
else printf("%d\n",ans);
return 0;
}
POI2013题解的更多相关文章
- 【题解】[P3557 POI2013]GRA-Tower Defense Game
[题解][P3557 POI2013]GRA-Tower Defense Game 这道题是真的** 根据题目给的\(k\),可以知道,我们随便放塔,只要不全放一起,一定是一种合法的方案. 直接枚举就 ...
- 题解 [POI2013]SPA-Walk
题目传送门 题目大意 给出两个长度为 \(n\) 的 \(01\) 串,问是否可以通过某一位把 \(s\) 变为 \(t\),但是中途不能变为 \(k\) 个 \(01\) 串中任意一个,问是否可行. ...
- 题解 luoguP3554 【[POI2013]LUK-Triumphal arch】
代码的关键部分 inline void dfs(int u,int fa) { ; for(int i=first[u]; i; i=nxt[i]) { int v=go[i]; if(v==fa)c ...
- [POI2013]Morskie opowieści
[POI2013]Morskie opowieści 题目大意: 一个\(n(n\le5000)\)点\(m(m\le5000)\)边无向图,边权均为\(1\),有\(k(k\le10^6)\)个询问 ...
- 【BZOJ3416】Poi2013 Take-out 栈
[BZOJ3416]Poi2013 Take-out Description 小F喜欢玩一个消除游戏——take-out 保证k+1|n,保证输入数据有解这是一个单人游戏 游戏者的目标是消除初始时给定 ...
- 【BZOJ3417】Poi2013 Tales of seafaring 分层图BFS
[BZOJ3417]Poi2013 Tales of seafaring Description 一个n点m边无向图,边权均为1,有k个询问 每次询问给出(s,t,d),要求回答是否存在一条从s到t的 ...
- 【BZOJ3425】Poi2013 Polarization 猜结论+DP
[BZOJ3425]Poi2013 Polarization Description 给定一棵树,可以对每条边定向成一个有向图,这张有向图的可达点对数为树上有路径从u到达v的点对(u,v)个数.求最小 ...
- 【BZOJ3417】[POI2013]MOR-Tales of seafaring (最短路SPFA)
[POI2013]MOR-Tales of seafaring 题目描述 一个n点m边无向图,边权均为1,有k个询问 每次询问给出(s,t,d),要求回答是否存在一条从s到t的路径,长度为d 路径不必 ...
- 2016 华南师大ACM校赛 SCNUCPC 非官方题解
我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...
随机推荐
- 34sqlite
sqlite,本地数据库.主要运用在小型的程序,传送方便(如发送附带数据库的程序,但MySQL有点大,或许没安装). 如何创建本地数据库? 1.新建一个.txt的文本文件. 2.直接将后缀名有.txt ...
- Transactions and beyond it..
While data integrity is managed very effectively within a single database with row locking, deadlock ...
- 382. Linked List Random Node(蓄水池采样)
1. 问题 给定一个单链表,随机返回一个结点,要求每个结点被选中的概率相等. 2. 思路 在一个给定长度的数组中等概率抽取一个数,可以简单用随机函数random.randint(0, n-1)得到索引 ...
- MVC相关资料收集
文章: 谈谈service层在mvc框架中的意义和职责 Model–view–controller - Wikipedia MVC Architecture - Google Chrome - Chr ...
- 经查-- git使用报错及解决办法
git push 错误 error: failed to push some refs to 'git@github.com:charblus/ ...' 本地和远程的文件应该合并后才能上传本地的新文 ...
- IAsyncResult接口
#region 程序集 mscorlib.dll, v4.0.0.0 // C:\Program Files (x86)\Reference Assemblies\Microsoft\Framewor ...
- java分布式系统开关功能设计(服务升降级)
问题一:在单个java系统中如何实现开关功能? 其实对于开关来说,对应Java中的类型,很好映射,就是一个boolean值,在需要做开关操作的地方,调用这个属性,判断状态,然后走相应的 ...
- API设计原则(觉得太合适,转发做记录)
API设计原则 对于云计算系统,系统API实际上处于系统设计的统领地位,正如本文前面所说,K8s集群系统每支持一项新功能,引入一项新技术,一定会新引入对应的API对象,支持对该功能的管理操作,理解掌握 ...
- SPOJ - PGCD Primes in GCD Table(莫比乌斯反演)
http://www.spoj.com/problems/PGCD/en/ 题意: 给出a,b区间,求该区间内满足gcd(x,y)=质数的个数. 思路: 设f(n)为 gcd(x,y)=p的个数,那么 ...
- spark 都用了哪些开源东东
直接上干货 commons-cli http://www.ibm.com/developerworks/cn/java/j-lo-commonscli/ guava http://www.cnblog ...