18th Central European Olympiad in Informatics
Tasks
Similarity | (Day 0) | (100/100) |
Balloons | (Day 1) | (100/100) |
Matching | (Day 1) | (100/100) |
Treasure Hunt | (Day 1) | (100/100) |
Hotel | (Day 2) | (100/100) |
Teams | (Day 2) | (100/100) |
Traffic | (Day 2) | (100/100) |
但是切完辣~~~~
搞笑来的题。随便怎么搞都行
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,num[26]; char a[2000005],b[2000005];
int main(){
scanf("%s%s",a+1,b+1);
int i; n=strlen(a+1); m=strlen(b+1);
for (i=1; i<=m-n+1; i++) num[b[i]-'a']++;
long long ans=num[a[1]-'a'];
for (i=m-n+2; i<=m; i++){
num[b[i-m+n-1]-'a']--; num[b[i]-'a']++;
ans+=num[a[i-m+n]-'a'];
}
printf("%lld\n",ans);
return 0;
}
Balloons
随便列一个方程就能知道r[i]=min{(a[i]-a[j])^2/4/r[j]},然后感性理解吹气球的过程,不妨从当前有用的气球一个个尝试,如果比当前有用的气球最右边一个半径大那么那个气球就没用了。这样一直做下去直到比最右边那个要小,这个时候就是膨胀的极限了。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200005
using namespace std;
int n,q[N]; double a[N],r[N];
int main(){
scanf("%d",&n);
int i,j,tail=0;
for (i=1; i<=n; i++){
scanf("%lf%lf",&a[i],&r[i]);
while (tail){
j=q[tail]; r[i]=min(r[i],(a[i]-a[j])*(a[i]-a[j])/4/r[j]);
if (r[i]>=r[j]) tail--; else break;
}
printf("%.3f\n",r[i]); q[++tail]=i;
}
return 0;
}
Matching
拓展kmp,,根本想不到啊。
首先预处理出模板中每一位,求出它之前比它小的最大的那个,以及比它大的最小的那个作为前后驱。然后用kmp一样的方法处理即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1000005
using namespace std;
int n,m,cnt,pre[N],nxt[N],ans[N],lf[N],rg[N],a[N],b[N],c[N],q[N],f[N];
int read(){
int x=0; char cr=getchar();
while (cr<'0' || cr>'9') cr=getchar();
while (cr>='0' && cr<='9'){ x=x*10+cr-'0'; cr=getchar(); }
return x;
}
bool ok(int *a,int x,int y){
return (!lf[x] || a[y+lf[x]]<a[y]) && (!rg[x] || a[y+rg[x]]>a[y]);
}
int main(){
m=read(); n=read();
int i,j;
for (i=1; i<=m; i++) b[c[i]=read()]=i;
for (i=1; i<=n; i++) a[i]=read();
for (i=1; i<=m; i++){ pre[i]=i-1; nxt[i]=i+1; }
for (i=m; i; i--){
j=b[i];
if (pre[j]) lf[i]=c[pre[j]]-i; if (nxt[j]<=m) rg[i]=c[nxt[j]]-i;
pre[nxt[j]]=pre[j]; nxt[pre[j]]=nxt[j];
}
for (i=2; i<=m; i++){
f[i]=f[i-1]; while (f[i] && !ok(b,f[i]+1,i)) f[i]=f[f[i]];
if (ok(b,f[i]+1,i)) f[i]++;
}
for (i=1,j=0; i<=n; i++){
while (j && !ok(a,j+1,i)) j=f[j];
if (ok(a,j+1,i)) j++;
if (j==m){ ans[++cnt]=i-m+1; j=f[j]; }
}
printf("%d\n",cnt);
for (i=1; i<=cnt; i++) printf("%d%c",ans[i],(i<cnt)?' ':'\n');
return 0;
}
Treasure Hunt
由于要兹瓷动态加块,而且显然要求lca,那就不能用树剖或者rmq了。。考虑倍增,令fa[x][i]表示x这个块向上走2^i步到达的块,dad[x][i]表示x这个块向上走2^i步走到的点。这样就可以很方便地处理了。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 400005
using namespace std;
int cnt,bin[36],fa[N][19],dad[N][19],d[N],dep[N];
struct node{ int l,r; }a[N];
void init(){
a[cnt=1].l=a[1].r=1; d[cnt]=dep[cnt]=1;
int i;
bin[0]=1; for (i=1; i<=19; i++) bin[i]=bin[i-1]<<1;
}
int find(int x){
int l=1,r=cnt,mid;
while (l<r){
mid=(l+r)>>1;
if (x<=a[mid].r) r=mid; else l=mid+1;
}
return l;
}
void path(int x,int len){
cnt++;
a[cnt].l=a[cnt-1].r+1; a[cnt].r=a[cnt-1].r+len;
fa[cnt][0]=find(x); dad[cnt][0]=x;
d[cnt]=d[fa[cnt][0]]+x-a[fa[cnt][0]].l+1;
dep[cnt]=dep[fa[cnt][0]]+1;
int i;
for (i=1; i<=18; i++){
fa[cnt][i]=fa[fa[cnt][i-1]][i-1]; dad[cnt][i]=dad[fa[cnt][i-1]][i-1];
}
}
int dig(int x,int y){
int u=find(x),v=find(y),dx=d[u]+x-a[u].l,dy=d[v]+y-a[v].l,p=x,q=y;
if (dep[u]<dep[v]){ swap(u,v); swap(x,y); }
int tmp=dep[u]-dep[v],i;
for (i=0; i<=18; i++) if (tmp&bin[i]){
x=dad[u][i]; u=fa[u][i];
}
for (i=18; i>=0; i--) if (fa[u][i]!=fa[v][i]){
x=dad[u][i]; y=dad[v][i];
u=fa[u][i]; v=fa[v][i];
}
if (u!=v){
x=dad[u][0]; y=dad[v][0];
u=fa[u][0]; v=fa[v][0];
}
tmp=dx+dy-(d[u]+min(x,y)-a[u].l<<1);
x=p; y=q;
if (dx<dy){ swap(x,y); swap(dx,dy); tmp=tmp+1>>1; }
else tmp>>=1;
tmp=dx-tmp;
u=find(x);
for (i=18; i>=0; i--)
if (d[fa[u][i]]+dad[u][i]-a[fa[u][i]].l>=tmp) u=fa[u][i];
return a[u].l+tmp-d[u];
}
Hotel
最朴素的贪心。。然而是对的。
考虑将订单按价格从高到低排序,然后按顺序安排最便宜房间并求出收益。然后把最大的o个收益加起来就好了。这样显然是对的,因为如果房间有冲突,显然会满足价格最高的;另一方面如果2个订单对2个房间可以任意分配,那么将价格高的订单分配便宜的房间或是贵的并没有影响。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 500005
using namespace std;
int n,m,cnt,ans[N],fa[N];
struct node{ int x,y; }a[N],b[N];
int read(){
int x=0; char cr=getchar();
while (cr<'0' || cr>'9') cr=getchar();
while (cr>='0' && cr<='9'){ x=x*10+cr-'0'; cr=getchar(); }
return x;
}
bool cmpx(const node &u,const node &v){ return u.x>v.x; }
bool cmpy(const node &u,const node &v){ return u.y<v.y || u.y==v.y && u.x<v.x; }
int getfa(int x){ return (x==fa[x])?x:fa[x]=getfa(fa[x]); }
int find(int x){
int l=1,r=n+1,mid;
while (l<r){
mid=(l+r)>>1;
if (a[getfa(mid)].y<x) l=mid+1; else r=mid;
}
return getfa(l);
}
int main(){
n=read(); m=read(); cnt=read();
int i,j;
for (i=1; i<=n; i++){
a[i].x=read(); a[i].y=read();
}
for (i=1; i<=m; i++){
b[i].x=read(); b[i].y=read();
}
sort(a+1,a+n+1,cmpy); sort(b+1,b+m+1,cmpx);
for (i=1; i<=n+1; i++) fa[i]=i; a[n+1].y=1000000000;
for (i=1; i<=m; i++){
j=find(b[i].y);
if (j<=n && a[j].x<b[i].x){
ans[i]=b[i].x-a[j].x; fa[j]=j+1;
}
}
sort(ans+1,ans+m+1);
long long sum=0;
for (i=m-cnt+1; i<=m; i++) sum+=ans[i];
printf("%lld\n",sum);
return 0;
}
Teams
O(N)的思路好神啊。。波兰人怎么这么熟练啊根本想不到(只会带log的sb做法)
首先按照a[i]降序排序,然后令f[i]表示前i个最多分成几组。显然f[i-1]<=f[i]<=f[i-1]+1,考虑f[i]=f[i-1]+1的条件,一定是存在f[j]==f[i-1]且j+a[j+1]==i,这样所有i向i+a[i+1]连边,最多N个转移。
然后考虑最大组人数最小,不妨设为g[i]。如果f[i]=f[i-1],显然可以把i塞到前面随便什么地方,如果已经塞满了那么g[i]=g[i-1]+1;否则g[i]=g[i-1]。如果f[i]=f[i-1]+1,那么显然g[i]=max(g[x],i-x),其中f[x]==f[i-1]且x+a[x+1]==i。
然后打个表示表示如果f[i]=f[i-1]+1那么i是从那个x转移来的然后把x+1~i的都编成一组。剩下的按照a[i]从大到小塞入从大到小的组中即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1000005
using namespace std;
int n,cnt,fst[N],nxt[N],a[N],b[N],f[N],g[N],p[N],sz[N],ans[N]; bool bo[N];
int read(){
int x=0; char cr=getchar();
while (cr<'0' || cr>'9') cr=getchar();
while (cr>='0' && cr<='9'){ x=x*10+cr-'0'; cr=getchar(); }
return x;
}
void add(int x,int y){ nxt[y]=fst[x]; fst[x]=y; }
int main(){
n=read();
int i,x;
for (i=1; i<=n; i++){
x=read(); add(x,i);
}
for (i=n; i; i--)
for (x=fst[i]; x; x=nxt[x]){ a[++cnt]=i; b[cnt]=x; }
memset(fst,0,sizeof(fst)); memset(p,-1,sizeof(p));
f[a[1]]=1; g[a[1]]=a[1]; p[a[1]]=0;
add(a[1]+a[a[1]+1],a[1]);
for (i=a[1]+1; i<=n; i++){
f[i]=f[i-1]; g[i]=((long long)f[i-1]*g[i-1]==i-1)?g[i-1]+1:g[i-1];
for (x=fst[i]; x; x=nxt[x])
if (f[x]+1>f[i]){
f[i]=f[x]+1; g[i]=max(g[x],i-x); p[i]=x;
} else if (f[x]+1==f[i] && max(g[x],i-x)<g[i]){
g[i]=max(g[x],i-x); p[i]=x;
}
add(i+a[i+1],i);
}
printf("%d\n",f[n]);
memset(fst,0,sizeof(fst)); memset(bo,1,sizeof(bo));
for (i=n,cnt=0; i>=a[1]; i=(p[i]>=0)?p[i]:i-1)
if (p[i]>=0){
cnt++;
for (x=i; x>p[i]; x--){ add(cnt,b[x]); sz[cnt]++; bo[x]=0; }
}
for (i=1,x=cnt; i<=n; i++) if (bo[i]){
while (sz[x]==g[n]) x--;
add(x,b[i]); sz[x]++;
}
for (i=1; i<=cnt; i++){
printf("%d",sz[i]);
for (x=fst[i]; x; x=nxt[x]) printf(" %d",x); puts("");
}
return 0;
}
Traffic
题目中的关键在于任意两条路没有交叉。
这说明一个点能到达的右侧的点一定是连续的(永远到不了的除外)。
那么tarjan缩点以后dp出每个点能到达的最小和最大的编号即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 300005
#define M 1800005
using namespace std;
int n,m,ta,tb,cnt,pt,tot,dfsclk,fst[N],pnt[M],nxt[M],tp,stk[N];
int mx[N],mn[N],scc[N],q[N],id[N],pos[N],low[N],a[N],b[N]; bool vis[N];
struct node{ int x,y,z; }e[M>>1];
int read(){
int x=0; char cr=getchar();
while (cr<'0' || cr>'9') cr=getchar();
while (cr>='0' && cr<='9'){ x=x*10+cr-'0'; cr=getchar(); }
return x;
}
void add(int x,int y){
pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void dfs(int x){
if (vis[x]) return; vis[x]=1;
int i;
for (i=fst[x]; i; i=nxt[i]) dfs(pnt[i]);
}
void tarjan(int x){
int i,y; pos[x]=low[x]=++dfsclk; stk[++tp]=x;
for (i=fst[x]; i; i=nxt[i]){
y=pnt[i];
if (!pos[y]){
tarjan(y); low[x]=min(low[x],low[y]);
} else if (!scc[y]) low[x]=min(low[x],pos[y]);
}
if (low[x]==pos[x]){
cnt++; mx[cnt]=-1000000000; mn[cnt]=1000000000;
while (1){
scc[stk[tp]]=cnt;
if (y=id[stk[tp]]){
mx[cnt]=max(mx[cnt],y); mn[cnt]=min(mn[cnt],y);
}
if (stk[tp--]==x) break;
}
}
}
void dp(int x){
if (vis[x]) return; vis[x]=1;
int i,y;
for (i=fst[x]; i; i=nxt[i]){
y=pnt[i];
dp(y); mx[x]=max(mx[x],mx[y]); mn[x]=min(mn[x],mn[y]);
}
}
bool cmp(const int &x,const int &y){ return b[x]>b[y]; }
int main(){
n=read(); m=read(); ta=read(); tb=read();
int i,x,y;
for (i=1; i<=n; i++){
a[i]=read(); b[i]=read();
}
for (i=1; i<=m; i++){
e[i].x=read(); e[i].y=read(); e[i].z=read();
add(e[i].x,e[i].y);
if (e[i].z==2) add(e[i].y,e[i].x);
}
for (i=1; i<=n; i++) if (!a[i]) dfs(i);
for (i=1; i<=n; i++) if (a[i]==ta && vis[i]) q[++pt]=i;
sort(q+1,q+pt+1,cmp);
for (i=1; i<=pt; i++) id[q[i]]=i;
for (i=1; i<=n; i++) if (!pos[i]) tarjan(i);
tot=0; memset(fst,0,sizeof(fst));
for (i=1; i<=m; i++){
x=scc[e[i].x]; y=scc[e[i].y];
if (x!=y) add(x,y);
}
memset(vis,0,sizeof(vis));
for (i=1; i<=cnt; i++) dp(i);
pt=0;
for (i=1; i<=n; i++) if (!a[i]) q[++pt]=i;
sort(q+1,q+pt+1,cmp);
for (i=1; i<=pt; i++) printf("%d\n",max(0,mx[scc[q[i]]]-mn[scc[q[i]]]+1));
return 0;
}