20211118 NOIP 灌水模拟赛
前言
信心赛一点也不影响我挂70分
同志们!double是四舍五入保留小数位!!!
T1
题面把我唬住了,想了半天没想出来。看一眼数据范围,这不是傻逼题吗??
取整挂了70分,我吐了
思路:
枚举流量然后dijkstra计算费用。\(O(n^2 \log n)\)水过去
#include<bits/stdc++.h>
#define in read()
using namespace std;
typedef long long ll;
inline int read(){
static char ch;
int res=0,sign=1;
while((ch=getchar())<'0'||ch>'9'){
if(ch=='-') sign=-1;
}
res=ch-'0';
while((ch=getchar())>='0'&&ch<='9'){
res=res*10+ch-'0';
}
return res*sign;
}
int n,m;
const int N=1e4;
const int INF=0x3f3f3f3f;
int head[N],to[N*2],w[N*2],nxt[N*2],cost[N*2];
int cnt;
void add(int u,int v,int z,int val){
nxt[++cnt]=head[u];
head[u]=cnt;
w[cnt]=z;
to[cnt]=v;
cost[cnt]=val;
}
int dis[N],vis[N];
int dijkstra(int mid){
priority_queue<pair<int,int> >q;
for(int i=1;i<=n;i++){
dis[i]=INF;
vis[i]=0;
}
dis[1]=0;
q.push(make_pair(0,1));
while(!q.empty()){
int u=q.top().second;
q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(dis[v]>dis[u]+w[i]&&cost[i]>=mid){
dis[v]=dis[u]+w[i];
q.push(make_pair(-dis[v],v));
}
}
}
return dis[n];
}
map<int,int> Q;
int main(){
freopen("pump.in","r",stdin);
freopen("pump.out","w",stdout);
n=in,m=in;
for(int i=1;i<=m;i++){
int a,b,c,d;
a=in,b=in,c=in,d=in;
Q[d]++;
add(a,b,c,d);
add(b,a,c,d);
}
double ans=0.0;
for(int i=1;i<=1000;i++){
if(Q[i]==0) continue;
double a=dijkstra(i);
double b=i;
if(!a) continue;
ans=max(ans,(b/a));
}
ans*=1000000;
ans=(int)ans;
printf("%.0lf\n",ans);
return 0;
}
T2
考前做过好无聊~~;
lca再加上树上前缀和即可
不放代码了
T3
T2的加强版;
思路有好几种,不过我比较喜欢树链剖分加上二分。\(O(n \log^2 n)\)
思路就是,vector记录每种颜色在欧拉序上出现的下标,每次二分查找vector\([id[top[a]],id[a]]\)之间有没有出现。
隔壁巨佬用树套树过了。\(O(n \log^3 n)\)
算法一:
#include<bits/stdc++.h>
#define in read()
#define int long long
using namespace std;
const int N=1e5+7;
inline int read(){
static char ch;
int res=0,sign=1;
while((ch=getchar())<'0'||ch>'9'){
if(ch=='-') sign=-1;
}
res=ch-'0';
while((ch=getchar())>='0'&&ch<='9'){
res=res*10+ch-'0';
}
return res*sign;
}
int a[N];
int head[N],nxt[N*2],to[N*2],e;
int top[N],fa[N],son[N],siz[N],id[N],atid[N],dep[N],cnt;
int ans[N],qwq=0;
vector<int>v[N];
int n,m;
void add(int u,int v){
to[++e]=v;
nxt[e]=head[u];
head[u]=e;
}
void dfs1(int u,int father){
siz[u]=1;
fa[u]=father;
son[u]=-1;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(v==father) continue;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]||son[u]==-1){
son[u]=v;
}
}
return ;
}
void dfs2(int u,int topf){
top[u]=topf;
id[u]=++cnt;
atid[cnt]=u;
if(son[u]) dfs2(son[u],topf);
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(v==fa[u]||v==son[u]){
continue;
}
dfs2(v,v);
}
}
signed main(){
n=in,m=in;
for(int i=1;i<=n;i++){
a[i]=in;
}
for(int i=1;i<n;i++){
int a,b;
a=in,b=in;
add(a,b);
add(b,a);
}
dfs1(1,1);
dfs2(1,1);
for(int i=1;i<=cnt;i++){
v[a[atid[i]]].push_back(i);
}
while(m--){
int a,b,c;
a=in,b=in,c=in;
int flag=0;
while(top[a]!=top[b]){
if(id[top[a]]<id[top[b]]) swap(a,b);
vector<int>::iterator it=lower_bound(v[c].begin(),v[c].end(),id[top[a]]);
if(it!=v[c].end()&&*it<=id[a]){
flag=1;
break;
}
a=fa[top[a]];
}
if(!flag){
if(id[a]>id[b]) swap(a,b);
vector<int>::iterator it=lower_bound(v[c].begin(),v[c].end(),id[a]);
if(it!=v[c].end()&&*it<=id[b]){
flag=1;
}
}
ans[++qwq]=flag;
}
for(int i=1;i<=qwq;i++){
cout<<ans[i];
}
return 0;
}
算法二:
#include<bits/stdc++.h>
using namespace std;
#define cs const
#define pb push_back
#define mp make_pair
#define ll long long
#define gc getchar
#define REP(i,x,y) for(int i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repp(i,n) for(int i=n;i>=1;i--)
#define repG(e) for(int e=hd[u];e;e=nt[e])
inline int read(){
int x=0;char v=gc();
while(!isdigit(v))v=gc();
while(isdigit(v))x=(x<<1)+(x<<3)+(v^48),v=gc();
return x;
}
cs int N=1e5+10;
int hd[N],nt[N<<1],to[N<<1],tot;
inline void add(int x,int y){
nt[++tot]=hd[x];
hd[x]=tot;
to[tot]=y;
}
int n,q;
int a[N];
int fa[N],sz[N],top[N],son[N],id[N],rev[N],dep[N],cnt;
inline void dfs1(int u,int f){
sz[u]=1;
fa[u]=f;
dep[u]=dep[f]+1;
repG(e)if(to[e]^f){
dfs1(to[e],u);
if(sz[to[e]]>sz[son[u]])son[u]=to[e];
}
sz[f]+=sz[u];
}
inline void dfs2(int u){
id[u]=++cnt;
rev[cnt]=u;
if(son[fa[u]]^u)top[u]=u;
else top[u]=top[fa[u]];
if(son[u])dfs2(son[u]);
repG(e)if((to[e]^fa[u])&&(to[e]^son[u]))dfs2(to[e]);
}
set<int>::iterator it;
class Tree{
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (l+r>>1)
public:
set<int>c[N<<2];
inline void pushup(int p){
for(it=c[lc].begin();it!=c[lc].end();it++)c[p].insert(*it);
for(it=c[rc].begin();it!=c[rc].end();it++)c[p].insert(*it);
}
inline void build(int p,int l,int r){
if(l==r)return(void)(c[p].insert(a[rev[l]]));
build(lc,l,mid);
build(rc,mid+1,r);
pushup(p);
}
inline bool Query(int p,int l,int r,int ql,int qr,int ki){
if(ql<=l&&r<=qr)return c[p].count(ki);
bool res=false;
if(ql<=mid)res=Query(lc,l,mid,ql,qr,ki);
if(qr>mid)res|=Query(rc,mid+1,r,ql,qr,ki);
return res;
}
}T;
inline bool Querylian(int u,int v,int ki){
bool res=false;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]])swap(u,v);
res|=T.Query(1,1,n,id[top[u]],id[u],ki);
u=fa[top[u]];
}
if(dep[u]>dep[v])swap(u,v);
res|=T.Query(1,1,n,id[u],id[v],ki);
return res;
}
int main(){
freopen("visit.in","r",stdin);
freopen("visit.out","w",stdout);
n=read(),q=read();
rep(i,n)a[i]=read();
int u,v,ki;
for(int i=1;i<n;i++)u=read(),v=read(),add(u,v),add(v,u);
dfs1(1,1);
dfs2(1);
T.build(1,1,n);
while(q--){
u=read(),v=read(),ki=read();
cout<<Querylian(u,v,ki);
}
fclose(stdin);fclose(stdout);
return 0;
}
T4:
最有意思的题,很妙。
思路是二分答案,然后线段树辅助建图后跑2-SAT板子,tarjan求强联通分量再判断。
2-SAT可以看板子。
线段树建图待会再说
思路就是对于每一个中心,把它与左右mid范围内的点的对立点连边,连边用线段树建图优化。
每次求强连通分量优化一下。
代码:
#include<bits/stdc++.h>
using namespace std;
#define clear(x) memset(x,0,sizeof (x))
#define op(x) ((x)<=n?(x)+n:(x)-n)
#define mid ((l+r)>>1)
#define ls now*2
#define rs now*2+1
const int N=4e4+10,M=N*20;
int head[N*5],nxt[M],t[M],ec;
void add(int u,int v){
t[++ec]=v;
nxt[ec]=head[u];
head[u]=ec;
}
struct Flag{
int pos,id;
bool operator < (const Flag &f) const {return pos<f.pos;}
Flag(int pos=0):pos(pos){}
}flgs[N*2];
int n,cnt,id[N*5];
void build(int now,int l,int r){
id[now]=++cnt;
if(l==r){
add(id[now],op(flgs[l].id));//每个点与他的对立点连边
return ;
}
build(ls,l,mid);
build(rs,mid+1,r);
add(id[now],id[ls]);
add(id[now],id[rs]);
}
void link(int now,int l,int r,int x,int y,int point){//对区间连边
if(y<x) return ;
if(l==x&&y==r) add(point,id[now]);
else if(y<=mid) link(ls,l,mid,x,y,point);
else if(x>mid) link(rs,mid+1,r,x,y,point);
else link(ls,l,mid,x,mid,point),link(rs,mid+1,r,mid+1,y,point);
}
#undef mid
int dfn[N*5],low[N*5],tim;
int stk[N*5],top;
int scc[N*5],col;
bool in[N*5];
void tarjan(int u){//缩点
dfn[u]=low[u]=++tim;
stk[++top]=u;
in[u]=1;
for(int i=head[u];i;i=nxt[i]){
int v=t[i];
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}else {
if(in[v]){
low[u]=min(low[u],dfn[v]);
}
}
}
if(dfn[u]==low[u]){
++col;
scc[u]=col;
while(stk[top]!=u){
scc[stk[top]]=col;
in[stk[top]]=0;
--top;
}
in[stk[top]]=0;
--top;
}
}
bool check(int v){
top=tim=ec=0;
clear(head);
clear(nxt);
clear(t);
clear(dfn);
clear(low);
build(1,1,cnt=2*n);
int l,r;
for(int i=1;i<=2*n;i++){
l=upper_bound(flgs+1,flgs+n*2+1,Flag(flgs[i].pos-v))-flgs;//二分查找到左右端点
r=upper_bound(flgs+1,flgs+n*2+1,Flag(flgs[i].pos+v-1))-flgs-1;
link(1,1,2*n,l,i-1,flgs[i].id),link(1,1,2*n,i+1,r,flgs[i].id);//左右区间连边
}
for(int i=1;i<=2*n;i++){
if(!dfn[i]) tarjan(i);
}
for(int i=1;i<=n;i++){
if(scc[i]==scc[i+n]) return 0;//判断
}
return 1;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>flgs[i].pos>>flgs[i+n].pos;
flgs[i].id=i;
flgs[i+n].id=i+n;
}
sort(flgs+1,flgs+n*2+1);
int l=0,r=flgs[2*n].pos-flgs[1].pos+1,mid,ans;
while(l<=r){//二分答案
mid=(l+r)>>1;
if(check(mid)) l=mid+1,ans=mid;
else r=mid-1;
}
cout<<ans<<endl;
return 0;
}