刷题记录 11 月合集
刷题记录 11 月合集
没啥时间记录了,趁着考 NOIP 前还有空赶紧记一下。
P1081 NOIP2012 提高组 开车旅行
先考虑暴力,每个点预处理出 set
或平衡树找排名在
考虑优化,可以按照倍增的方法,设:
为从城市 出发走 天, 先开车到达的城市。 为从城市 出发走 天, 先开车, 开了多少路。 为从城市 出发走 天, 先开车, 开了多少路。
类似倍增
定义
将暴力中的跳点换成倍增跳点,复杂度
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define inf 1e16
const int maxn=1e5+200;
int n,x0,m;
int h[maxn],s[maxn],x[maxn];
int f[25][maxn][2],da[25][maxn][2],db[25][maxn][2];
struct City
{
int id,al;
friend bool operator < (City a,City b){return a.al<b.al;}
};
int la,lb,ansid;
inline void calc(int S,int X)
{
int p=S;
la=0,lb=0;
for(int i=18;~i;i--)
{
if(f[i][p][0]&&la+lb+da[i][p][0]+db[i][p][0]<=X)
{
la+=da[i][p][0];
lb+=db[i][p][0];
p=f[i][p][0];
}
}
}
inline void pre()
{
multiset<City>s;
h[0]=inf,h[n+1]=-inf;
City st;
st.id=0,st.al=inf;s.insert(st),s.insert(st);
st.id=n+1,st.al=-inf;s.insert(st),s.insert(st);
for(int i=n;i;i--)
{
int ga,gb;
City now;
now.id=i,now.al=h[i];
s.insert(now);
set<City>::iterator p=s.lower_bound(now);
p--;
int lt=(*p).id,lh=(*p).al;
p++,p++;
int ne=(*p).id,nh=(*p).al;
p--;
if(llabs(nh-h[i])>=llabs(h[i]-lh))
{
gb=lt;p--,p--;
if(llabs(nh-h[i])>=llabs(h[i]-(*p).al)) ga=(*p).id;
else ga=ne;
}
else
{
gb=ne;p++,p++;
if(llabs(h[i]-(*p).al)>=llabs(lh-h[i])) ga=lt;
else ga=(*p).id;
}
f[0][i][0]=ga,f[0][i][1]=gb;
da[0][i][0]=llabs(h[ga]-h[i]);
db[0][i][1]=llabs(h[gb]-h[i]);
}
// for(int i=1;i<=n;i++) cerr<<f[0][i][0]<<" "<<f[0][i][1]<<"\n";
for(int i=1;i<=18;i++)
{
for(int j=1;j<=n;j++) for(int k=0;k<2;k++)
{
if(i==1)
{
f[1][j][k]=f[0][f[0][j][k]][1-k];
da[1][j][k]=da[0][j][k]+da[0][f[0][j][k]][1-k];
db[1][j][k]=db[0][j][k]+db[0][f[0][j][k]][1-k];
}
else
{
f[i][j][k]=f[i-1][f[i-1][j][k]][k];
da[i][j][k]=da[i-1][j][k]+da[i-1][f[i-1][j][k]][k];
db[i][j][k]=db[i-1][j][k]+db[i-1][f[i-1][j][k]][k];
}
}
}
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&h[i]);
scanf("%lld%lld",&x0,&m);
for(int i=1;i<=m;i++) scanf("%lld%lld",&s[i],&x[i]);
pre();
double ans=inf*1.0;
for(int i=1;i<=n;i++)
{
calc(i,x0);
double nowans=(double)la/(double)lb;
if(nowans<ans)
{
ans=nowans;
ansid=i;
}
else if(nowans==ans&&h[ansid]<h[i]) ansid=i;
}
printf("%lld\n",ansid);
for(int i=1;i<=m;i++)
{
calc(s[i],x[i]);
printf("%lld %lld\n",la,lb);
}
}
本题有启发性,体现了从暴力到倍增优化的思想。
P9870 NOIP2023 双序列拓展
将问题进行转化,考虑成一个矩阵,其中第
在矩阵上做双指针,满足:
,向 走一步。 ,向 走一步。 ,向 走一步。
设
时间复杂度
考虑找到
接下来
按照类似的方法递归求解。
维护方面,维护出前缀最小值和后缀最大值可以简单的解决本题。
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+5;
struct node
{
int mi,mx;
}sufX[maxn],sufY[maxn],preX[maxn],preY[maxn];
int c,n,m,q;
int tf[maxn],tg[maxn],ttf[maxn],ttg[maxn],g[maxn],f[maxn];
inline node updata(bool flg,node a,node b)
{
if(a.mi==0||a.mx==0) return b;
node res=a;
if(!flg)
{
if(f[b.mi]<=f[res.mi]) res.mi=b.mi;
if(f[b.mx]>=f[res.mx]) res.mx=b.mx;
}
else
{
if(g[b.mi]<=g[res.mi]) res.mi=b.mi;
if(g[b.mx]>=g[res.mx]) res.mx=b.mx;
}
return res;
}
inline bool check1(int x,int y,int n,int m)
{
if(x==1||y==1) return true;
node X=preX[x-1],Y=preY[y-1];
if(f[X.mi]<g[Y.mi]) return check1(X.mi,y,n,m);
if(g[Y.mx]>f[X.mx]) return check1(x,Y.mx,n,m);
return false;
}
inline bool check2(int x,int y,int n,int m)
{
if(x==n||y==m) return true;
node X=sufX[x+1],Y=sufY[y+1];
if(f[X.mi]<g[Y.mi]) return check2(X.mi,y,n,m);
if(g[Y.mx]>f[X.mx]) return check2(x,Y.mx,n,m);
return false;
}
inline bool solve(int tmpf[],int tmpg[],int n,int m)
{
memset(preX,0,sizeof(preX));
memset(preY,0,sizeof(preY));
memset(sufX,0,sizeof(sufX));
memset(sufY,0,sizeof(sufY));
if(tmpf[1]>=tmpg[1]) return false;
for(int i=1;i<=n;i++) f[i]=tmpf[i];
for(int i=1;i<=m;i++) g[i]=tmpg[i];
for(int i=1;i<=n;i++) preX[i]=updata(0,preX[i-1],{i,i});
for(int i=1;i<=m;i++) preY[i]=updata(1,preY[i-1],{i,i});
for(int i=n;i>=1;i--) sufX[i]=updata(0,sufX[i+1],{i,i});
for(int i=m;i>=1;i--) sufY[i]=updata(1,sufY[i+1],{i,i});
node X=preX[n],Y=preY[m];
if(f[X.mi]>=g[Y.mi]||g[Y.mx]<=f[X.mx]) return false;
return check1(X.mi,Y.mx,n,m)&&check2(X.mi,Y.mx,n,m);
}
int main()
{
// freopen("P9870_5.in","r",stdin);
// freopen("P9870.out","w",stdout);
scanf("%d%d%d%d",&c,&n,&m,&q);
for(int i=1;i<=n;i++) scanf("%d",&tf[i]);
for(int i=1;i<=m;i++) scanf("%d",&tg[i]);
putchar((solve(tf,tg,n,m)||solve(tg,tf,m,n))?'1':'0');
while(q--)
{
for(int i=1;i<=n;i++) ttf[i]=tf[i];
for(int i=1;i<=m;i++) ttg[i]=tg[i];
int kx,ky;
scanf("%d%d",&kx,&ky);
while(kx--){int x,y;scanf("%d%d",&x,&y);ttf[x]=y;}
while(ky--){int x,y;scanf("%d%d",&x,&y);ttg[x]=y;}
putchar((solve(ttf,ttg,n,m)||solve(ttg,ttf,m,n))?'1':'0');
}
}
P9120 春季测试 2023 密码锁
随机化序列,枚举每一位移动的值,贪心的取。
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+5;
int n,k;
int a[maxn][10],mn[10],mx[10];
int main()
{
srand(114514);
int _;
scanf("%d%d",&_,&k);
while(_--)
{
scanf("%d",&n);
for(int i=1;i<=k;i++) for(int j=1;j<=n;j++) scanf("%d",&a[j][i]);
int tot=k==4?600:200;
int ans=1e9;
while(tot--)
{
random_shuffle(a+1,a+n+1);
memset(mx,-0x3f,sizeof(mx));
memset(mn,0x3f,sizeof(mn));
for(int i=1;i<=n;i++)
{
int sum=1e9,p=0;
for(int x=0;x<k;x++)
{
int tmp=0;
for(int j=1;j<=k;j++)
{
int id=(j+x-1)%k+1;
tmp=max(tmp,max(mx[j],a[i][id])-min(mn[j],a[i][id]));
}
if(tmp<sum) sum=tmp,p=x;
}
for(int j=1;j<=k;j++)
{
int id=(j+p-1)%k+1;
mx[j]=max(mx[j],a[i][id]);
mn[j]=min(mn[j],a[i][id]);
}
if(ans<sum) break;
}
int ret=0;
for(int i=1;i<=k;i++)
ret=max(ret,mx[i]-mn[i]);
ans=min(ans,ret);
}
printf("%d\n",ans);
}
}
P7962 NOIP2021 方差
不难发现一次操作的本质是交换相邻两项的差分,可以证明:
相邻三项
令第二项为
也就是可以任意交换两项,由于是求最小值,考虑退火。
每次随机交换两项,暴力此次的结果。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
const int maxn=1e4+5;
ll ans;
int n,l,r,len,mn;
int a[maxn],d[maxn],cf[maxn],tmp[maxn];
deque<int>que;
double down=0.97;
inline void fr()
{
l=r=0;
for(int i=1;i<n;i++)
if(mn==cf[i]&&!l) l=r=i;
else if(mn==cf[i]) r=i;
len=r-l+1;
}
inline ll calc()
{
ll now=0,sum=0;
for(int i=1;i<n;i++) now+=cf[i],sum+=now;
int aver=sum/n;
now=0,sum=0;
sum=aver*aver;
for(int i=1;i<n;i++) now+=cf[i],sum+=(now-aver)*(now-aver);
return sum/n;
}
inline void simulateAnneal()
{
len=r-l+1;
for(double T=2e5;T>1e-5;T*=down)
{
for(int i=1;i<n;i++){
if(cf[i]==mn){
l=i,r=l+len-1;
break;
}
}
for(int i=1;i<n;i++) tmp[i]=cf[i];
int pos=rand()%(n-1-len)+1;
if(pos>=l)
{
pos+=len;int t=cf[pos];
for(int i=pos;i;i--)
{
if(cf[i-1]>t||i==1) {cf[i]=t;break;}
cf[i]=cf[i-1];
}
}
else
{
int t=cf[pos];
for(int i=pos;i<n;i++)
{
if(cf[i+1]>t||i==n-1) {cf[i]=t;break;}
cf[i]=cf[i+1];
}
}
ll res=calc();
ll dt=res-ans;
if(dt<0) ans=res;
else if(exp(-dt/T)*RAND_MAX<=rand())
for(int i=1;i<n;i++) cf[i]=tmp[i];
}
}
signed main()
{
srand(time(0));
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),a[i]*=n;
int st=clock();
for(int i=2;i<=n;i++) d[i]=a[i]-a[i-1];
sort(d+2,d+n+1);
mn=d[2];
for(int i=2;i<=n;i++)
{
if(i&1) que.push_back(d[i]);
else que.push_front(d[i]);
}
for(int i=1;!que.empty();i++,que.pop_front()) cf[i]=que.front();
fr();
ans=calc();
if(len==n-1) printf("%lld",ans),exit(0);
while(clock()-st<950*1000) simulateAnneal();
printf("%lld",ans);
}
P8866 NOIP2022 喵了个喵
P1514 NOIP2010 提高组 引水入城
一开始证明部分想歪了。
第一行建满蓄水厂,枚举起点 bfs 判断所有蓄水厂能否覆盖第
第二问发现是每个蓄水厂可以覆盖第
图中的蓝色点可以通过蓝色线覆盖两个黄色点,若第
所以每个蓄水厂覆盖了一段第
#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define fi first
#define se second
const int maxn=505;
int n,m;
int a[maxn][maxn],fx[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
pii t[maxn];
bool vis[maxn][maxn],cis[maxn];
inline bool pd(int x,int y)
{
if(x<1||y<1||x>n||y>m) return 1;
return 0;
}
inline void bfs(int x,int y)
{
memset(vis,0,sizeof(vis));
queue<pii>que;
que.push({x,y});
vis[x][y]=1;
while(!que.empty())
{
int x=que.front().fi,y=que.front().se;
que.pop();
for(int i=0;i<4;i++)
{
int xx=x+fx[i][0],yy=y+fx[i][1];
if(vis[xx][yy]||pd(xx,yy)) continue;
if(a[xx][yy]<a[x][y])
{
vis[xx][yy]=1;
que.push({xx,yy});
}
}
}
t[y].fi=1e9;
for(int i=1;i<=m;i++)
{
cis[i]|=vis[n][i];
if(vis[n][i]) t[y].fi=min(t[y].fi,i),t[y].se=max(t[y].se,i);
}
}
int main()
{
// freopen("P1514_1.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
for(int i=1;i<=m;i++) bfs(1,i);
int ans=0;
for(int i=1;i<=m;i++) ans+=cis[i];
if(ans<m) {printf("0\n%d",m-ans);return 0;}
sort(t+1,t+m+1);
int r=0,id=0,rmx=0;
ans=0;
for(int i=1;i<=m;i++)
{
while(t[id+1].fi<=i&&id+1<=m) id++,rmx=max(rmx,t[id].se);
if(r<i) r=rmx,ans++;
}
printf("1\n%d",ans);
}
P1966 NOIP2013 提高组 火柴排队
拆式子。
转化为求
将
如何求交换最少?
设
在降序排序过程中,将
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define fi first
#define se second
const ll mod=1e8-3;
const int maxn=1e5+5;
struct treearray
{
#define N 100000
int ts[maxn];
inline int lowbit(int x){return x&(-x);}
inline void updata(int x,int y){for(;x<=N;x+=lowbit(x)) ts[x]+=y;}
inline int getsum(int x){int sum=0;for(;x;x-=lowbit(x)) sum+=ts[x];return sum;}
}T;
ll ans;
int n;
int p[maxn];
pii a[maxn],b[maxn];
map<int,int>mp;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i].fi),a[i].se=i;
for(int i=1;i<=n;i++) scanf("%d",&b[i].fi),b[i].se=i;
sort(a+1,a+n+1),sort(b+1,b+n+1);
for(int i=1;i<=n;i++) p[a[i].se]=b[i].se,mp[p[a[i].se]]=1;
auto it1=mp.begin(),it2=it1;
for(it2++;it2!=mp.end();it1++,it2++) it2->second+=it1->second;
for(int i=1;i<=n;i++)
{
p[i]=mp[p[i]];
ans+=T.getsum(n)-T.getsum(p[i]);
T.updata(p[i],1);
ans%=mod;
}
printf("%d",ans);
}
P1967 NOIP2013 提高组 货车运输
求树上两点间最小边权,倍增、树剖、Kruskal 重构树均可解决本题。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 20005, maxe = 100005;
struct node {
int to, fr, val;
} edge[maxe * 2];
struct node1 {
int to, nxt, val;
} ed[maxe * 2];
int n, m, tot, K = 15, q;
int f[maxn], head[maxn], deep[maxn], dis[maxn][50], fa[maxn][50];
bool vis[maxn];
bool cmp(node x, node y) { return x.val > y.val; }
void add(int a, int b, int c) {
tot++;
ed[tot].to = b;
ed[tot].val = c;
ed[tot].nxt = head[a];
head[a] = tot;
}
int findroot(int x) {
if (f[x] == x)
return x;
return f[x] = findroot(f[x]);
}
void maxtree() {
for (int i = 1; i <= n; i++) f[i] = i;
for (int i = 1; i <= m; i++) {
int x = edge[i].fr;
int y = edge[i].to;
int xf = findroot(x), yf = findroot(y);
if (xf != yf) {
add(x, y, edge[i].val);
add(y, x, edge[i].val);
f[yf] = f[xf];
}
}
}
void dfs(int x) {
vis[x] = true;
for (int i = head[x]; i; i = ed[i].nxt) {
int v = ed[i].to;
if (!vis[v]) {
deep[v] = deep[x] + 1;
dis[v][0] = ed[i].val;
fa[v][0] = x;
for (int j = 1; j < K; j++) {
fa[v][j] = fa[fa[v][j - 1]][j - 1];
dis[v][j] = min(dis[v][j - 1], dis[fa[v][j - 1]][j - 1]);
}
dfs(v);
}
}
}
int getlca(int x, int y) {
if (findroot(x) != findroot(y))
return -1;
int res = 1e9;
if (deep[x] < deep[y])
swap(x, y);
for (int i = K - 1; i >= 0; i--) {
if (fa[x][i] != 0 && deep[fa[x][i]] >= deep[y]) {
res = min(dis[x][i], res);
x = fa[x][i];
}
}
if (x == y)
return res;
for (int i = K - 1; i >= 0; i--) {
if (fa[x][i] != fa[y][i]) {
res = min(dis[x][i], res);
res = min(dis[y][i], res);
x = fa[x][i];
y = fa[y][i];
}
}
res = min(dis[x][0], res);
res = min(dis[y][0], res);
return res;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
edge[i].to = y;
edge[i].fr = x;
edge[i].val = z;
}
sort(edge + 1, edge + m + 1, cmp);
maxtree();
deep[1] = 1;
memset(dis, 0x7f, sizeof(dis));
for(int i=1;i<=n;i++) if(!vis[i]) dfs(i);
scanf("%d", &q);
for (int i = 1; i <= q; i++) {
int x, y;
scanf("%d%d", &x, &y);
printf("%d\n", getlca(x, y));
}
}
P5022 NOIP2018 提高组 旅行
如果在树上,贪心的进入较小的编号的儿子遍历。
在基环树上,发现可以去除一条边,当作树的情况。
#include<bits/stdc++.h>
using namespace std;
#define int short
const int maxn=5005;
struct Edge
{
int tot;
int head[maxn];
struct edgenode{int to,nxt,id;}edge[maxn*2];
inline void add(int u,int v,int id)
{
tot++;
edge[tot].to=v;
edge[tot].nxt=head[u];
edge[tot].id=id;
head[u]=tot;
}
}T;
inline int read()
{
int fx=1,sum=0;
char c=getchar();
while(c!='-'&&(c<'0'||c>'9')) c=getchar();
if(c=='-') fx=-1,c=getchar();
while('0'<=c&&c<='9') sum=(sum<<3)+(sum<<1)+c-'0',c=getchar();
return fx*sum;
}
int n,m,flg;
bool vis[maxn],blk[maxn];
vector<int>vec,ans;
inline void dfs(int u)
{
vis[u]=true;
vec.emplace_back(u);
if(ans.size()&&ans[vec.size()-1]<vec.back()&&flg==0) {flg=1;return ;}
if(ans.size()&&ans[vec.size()-1]>vec.back()&&flg==0) flg=-1;
vector<int>tmp;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(vis[v]||blk[T.edge[i].id]) continue;
tmp.push_back(v);
}
sort(tmp.begin(),tmp.end());
for(auto v:tmp)
{
if(vis[v]) continue;
dfs(v);
if(flg==1) return ;
}
}
signed main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
{
int x,y;
x=read(),y=read();
T.add(x,y,i),T.add(y,x,i);
}
if(m==n)
{
for(int i=1;i<=m;i++)
{
blk[i]=true;flg=0;
memset(vis,0,sizeof(vis));
vec.clear();
dfs(1);
blk[i]=false;
if(vec.size()<n) continue;
if(ans.size()==0) ans=vec;
else
{
for(int j=0;j<n;j++)
if(ans[j]>vec[j]) {ans=vec;break;}
else if(vec[j]>ans[j]) break;
}
}
}
else dfs(1),ans=vec;
for(auto v:ans) printf("%d ",v);
}
P1315 NOIP2011 提高组 观光公交
使用一次加速惠及到的人,是加速后到目的地无需等待人上车的站(包括第一个需要等待的站)。
倒序枚举每个加速器使用的段,继承维护惠及的人数,选择最大的段放。
放完后暴力维护一次信息。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
#define fi first
#define se second
const int maxn=1e3+5;
int n,m,k,ans;
int t[maxn],d[maxn],arr[maxn],tot[maxn];
vector<int>vec[maxn];
signed main()
{
// freopen("P1315_5.in","r",stdin);
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<n;i++) scanf("%lld",&d[i]);
for(int i=1;i<=m;i++)
{
int tim,x,y;
scanf("%lld%lld%lld",&tim,&x,&y);
vec[y].push_back(tim);
t[x]=max(t[x],tim);
tot[y]++;
}
for(int i=1;i<=n;i++) arr[i]=max(arr[i-1],t[i-1])+d[i-1];
for(int i=1;i<=k;i++)
{
int lst=0,ans=0,ansid=0;
for(int j=n-1;j;j--)
{
if(arr[j+1]>t[j+1]) lst+=tot[j+1];
else lst=tot[j+1];
if(lst>ans&&d[j]) ans=lst,ansid=j;
}
d[ansid]--;
for(int j=ansid+1;j<=n;j++) arr[j]=max(arr[j-1],t[j-1])+d[j-1];
}
for(int i=1;i<=n;i++)
{
for(auto v:vec[i]) ans+=max(arr[i]-v,0ll);
}
printf("%lld",ans);
}
P7124 Ynoi2008 stcm
P7215 JOISC2020 首都
P7320 PMOI-4 可怜的团主
建出任意一颗生成树,将度为
否则将叶子两两任意配对,找到一个没有被覆盖的点
将其配对改为
可以覆盖原先覆盖的点,并新覆盖了点
所以本题总是有解的。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e3+5;
struct Edge
{
int tot;
int head[maxn];
struct edgenode{int to,nxt;}edge[maxn*maxn];
inline void add(int u,int v)
{
tot++;
edge[tot].to=v;
edge[tot].nxt=head[u];
head[u]=tot;
}
}G,T;
int n,m,ex,tot;
int deg[maxn],leaf[maxn],to[maxn];;
bool vis[maxn],col[maxn];
vector<int>ans;
inline void dfs_tree(int u)
{
vis[u]=true;
for(int i=G.head[u];i;i=G.edge[i].nxt)
{
int v=G.edge[i].to;
if(vis[v]) continue;
T.add(u,v);
T.add(v,u);
deg[u]++,deg[v]++;
dfs_tree(v);
}
}
inline bool dfs(int u,int ed,int f,int flg)
{
if(u==ed)
{
col[u]=1;
if(flg&&u!=ex) ans.push_back(u);
return true;
}
bool val=false;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(v==f) continue;
if(dfs(v,ed,u,flg))
{
col[u]|=1;
val=true;
break;
}
}
if(flg&&val&&u!=ex) ans.push_back(u);
return val;
}
inline int fr(int u,int f)
{
if(deg[u]==1) return u;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(v==f) continue;
return fr(v,u);
}
}
inline void work(int u,int v)
{
to[u]=v,to[v]=u;
dfs(u,v,0,0);
}
int need;
int main()
{
scanf("%d%d",&n,&m);
need=(n+5)/6;
for(int i=1;i<=m;i++)
{
int v,u;
scanf("%d%d",&u,&v);
G.add(u,v);
G.add(v,u);
}
dfs_tree(1);
for(int i=2;i<=n;i++)
{
if(deg[i]==1) leaf[++tot]=i;
}
if(tot>=n/3)
{
puts("2");
for(int i=1;i<=n/3;i++) printf("%d ",leaf[i]);
return 0;
}
if(deg[1]==1) leaf[++tot]=1;
if(tot&1) T.add(1,++n),deg[n]=1,leaf[++tot]=n,ex=n;
for(int i=1;i<=n;i+=2)
{
work(leaf[i],leaf[i+1]);
}
while(1)
{
int g,u[3],v[3];
bool flg=true;
for(int i=1;i<=n;i++)
{
if(!col[i])
{
g=i;flg=false;
break;
}
}
if(flg) break;
int cnt=0;
for(int i=T.head[g];i;i=T.edge[i].nxt)
{
int gv=T.edge[i].to;
u[++cnt]=fr(gv,g);v[cnt]=to[u[cnt]];
if(cnt==2) break;
}
work(u[1],v[2]),work(v[1],u[2]);
}
puts("1");
for(int i=1;i<=tot;i++)
{
int u=leaf[i];
int v=to[u];
if(u<v)
{
need--;
ans.clear();
dfs(u,v,0,1);
printf("%d ",(int)ans.size());
for(auto v:ans) printf("%d ",v);
putchar('\n');
}
}
for(int i=1;i<=need;i++) printf("1 %d\n",i);
}
P7393 TOCO Round 1 Eternal Star
钦定
设有
解得
递归处理即可得到正确的树。
点数过多,考虑减少儿子数量。
建出两个
相较于原来少了
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int cnt;
vector<int>E[maxn];
inline int dfs(int k,int ad)
{
int u=++cnt;
if(k==1) return u;
if(u==1) E[u].push_back(dfs(k-1,1));
for(int i=1;i<k-(u==1);i++)
{
for(int j=1;j<=k-i+1+ad;j++) E[u].push_back(dfs(i,0));
}
return u;
}
inline void out(int u){for(auto v:E[u]) printf("%d %d\n",u,v),out(v);}
int main()
{
int K,x;
scanf("%d%d",&K,&x);
dfs(K,0);
printf("%d\n",cnt);
out(1);
}
P1084 NOIP2012 提高组 疫情控制
要是 NOIP2024 也这样简单就好了……
最大值最小,二分时间
判断与根相连的儿子是否叶子节点都被未走到根的军队完全覆盖。
- 是,可以不用处理该儿子。
- 否,根需要向下派军队,这个军队需要满足到根以后至少还可以向下走
步, 为儿子与根相连的边权。当然如果使用这个儿子前往首都的军队,就无须 的限制。选择满足条件的军队中可以走的步数最少的军队。
用 set
实现,复杂度
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pli pair<ll,int>
#define fi first
#define se second
const int maxn=5e4+5;
struct Edge
{
int tot;
int head[maxn];
struct edgenode{int to,nxt,w;}edge[maxn*2];
inline void add(int x,int y,int z)
{
tot++;
edge[tot].to=y;
edge[tot].w=z;
edge[tot].nxt=head[x];
head[x]=tot;
}
}T;
int n,m;
int dep[maxn],fa[maxn][20],p[maxn];
ll dis[maxn][20];
inline void dfs(int u,int f)
{
dep[u]=dep[f]+1;fa[u][0]=f;
for(int i=1;i<=log2(dep[u]);i++)
fa[u][i]=fa[fa[u][i-1]][i-1],dis[u][i]=dis[u][i-1]+dis[fa[u][i-1]][i-1];
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(v==f) continue;
dis[v][0]=T.edge[i].w;
dfs(v,u);
}
}
vector<ll>vec[maxn];
int val[maxn];
inline void dfs_find(int u,int f)
{
int cnt=0;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
cnt++;
int v=T.edge[i].to;
if(v==f) continue;
dfs_find(v,u);
val[u]+=val[v];
}
if(cnt==1) val[u]++;
if(vec[u].size()&&dep[u]>2) val[u]=0;
}
inline bool check(ll mid)
{
for(int i=1;i<=n;i++) vec[i].clear(),val[i]=0;
for(int i=1;i<=m;i++)
{
int u=p[i];ll sum=mid;
for(int j=log2(dep[u]);~j;j--)
{
if(dis[u][j]<=sum&&dep[fa[u][j]]>=2) sum-=dis[u][j],u=fa[u][j];
}
vec[u].push_back(sum);
}
dfs_find(1,0);
if(!val[1]) return true;
multiset<pli>s,tmp;
for(int i=T.head[1];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
for(int j=0;j<vec[v].size();j++) s.insert({vec[v][j]-T.edge[i].w,v});
}
for(int i=T.head[1];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(val[v])
{
tmp.clear();
for(int j=0;j<vec[v].size();j++)
{
auto it=s.find({vec[v][j]-T.edge[i].w,v});
if(it==s.end()) continue;
tmp.insert(*it);
s.erase(it);
}
auto itt=tmp.begin();
auto its=s.lower_bound({T.edge[i].w,0});
if(itt!=tmp.end())
{
if((*itt).fi<=(*its).fi||its==s.end()) tmp.erase(itt);
else s.erase(its);
for(auto it=tmp.begin();it!=tmp.end();it++) s.insert(*it);
}
else
{
if(its==s.end()) return false;
s.erase(its);
}
}
}
return true;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
T.add(u,v,w),T.add(v,u,w);
}
dfs(1,0);
scanf("%d",&m);
for(int i=1;i<=m;i++) scanf("%d",&p[i]);
ll l=0,r=1e15,ans=-1;
while(l<=r)
{
ll mid=(l+r)>>1;
if(check(mid)) r=mid-1,ans=mid;
else l=mid+1;
}
printf("%lld",ans);
}
P7054 NWRRC2015 Graph
P2680 NOIP2015 提高组 运输计划
最大值最小问题,二分长度,所有长度大于
查询是否存在一条边长度为
标记使用树上差分,时间复杂度
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e5+5;
int n,m,cf[N];
vector<pair<int,int>> G[N];
int f[N][30],dfn[N],uid[N],tim,val[N],dis[N],dep[N];
struct node {
int u,v,lca,dis;
}Q[N];
void dfs(int u,int pa)
{
uid[dfn[u]=++tim]=u;
dep[u]=dep[f[u][0]=pa]+1;
for(int i=1;i<=19;i++)
f[u][i]=f[f[u][i-1]][i-1];
for(auto [v,w]:G[u]){
if(v==pa) continue;
dis[v]=dis[u]+w;
dfs(v,u);
val[v]=w;
}
}
int LCA(int u,int v)
{
if(dep[u]<dep[v]) swap(u,v);
for(int i=19;~i;i--)
if(dep[f[u][i]]>=dep[v])
u=f[u][i];
if(u==v) return u;
for(int i=19;~i;i--)
if(f[u][i]!=f[v][i])
u=f[u][i],v=f[v][i];
return f[u][0];
}
bool check(int md)
{
memset(cf,0,sizeof(cf));
int cnt=0,ans=0;
for(int i=1;i<=m;i++){
if(Q[i].dis>md){
cf[Q[i].u]++,cf[Q[i].v]++,cf[Q[i].lca]-=2;
ans=max(ans,Q[i].dis-md);
cnt++;
}
}
if(cnt==0) return 1;
for(int i=n;i>1;i--)
cf[f[uid[i]][0]]+=cf[uid[i]];
for(int i=2;i<=n;i++)
if(cf[i]==cnt&&dis[i]-dis[f[i][0]]>=ans)
return 1;
return 0;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1,u,v,w;i<n;i++){
cin>>u>>v>>w;
G[u].emplace_back(v,w);
G[v].emplace_back(u,w);
}
dfs(1,0);
for(int i=1;i<=m;i++){
cin>>Q[i].u>>Q[i].v;
Q[i].lca=LCA(Q[i].u,Q[i].v);
Q[i].dis=dis[Q[i].u]+dis[Q[i].v]-2*dis[Q[i].lca];
}
int l=0,r=1e10,ans=-1;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid))
ans=mid,r=mid-1;
else
l=mid+1;
}
cout<<ans<<'\n';
return 0;
}
P5021 NOIP2018 提高组 赛道修建
二分赛道长度,check
时每个点向父亲上传一条长度小于
贪心的正确性:如果少拼一条赛道,并且用其中较大值上传,那么上传后也该较大值也最多可以拼接成为一条赛道,不同用途产生的贡献时一样的。
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+5;
struct Edge
{
int tot;
int head[maxn];
struct edgenode{int to,nxt,w;}edge[maxn*2];
inline void add(int x,int y,int w)
{
tot++;
edge[tot].to=y;
edge[tot].w=w;
edge[tot].nxt=head[x];
head[x]=tot;
}
}T;
int n,m,now;
multiset<int>s[maxn];
inline int dfs(int u,int f)
{
s[u].clear();
s[u].insert(0);
int res=0;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(v==f) continue;
res+=dfs(v,u);
auto it=s[v].end();it--;
s[u].insert(*it+T.edge[i].w);
}
auto it=s[u].end();
for(it--;it!=s[u].end();it=s[u].end(),it--)
{
if(*it>=now) res++,s[u].erase(it);
else break;
}
auto nxt=s[u].begin();
for(auto it=s[u].begin();it!=s[u].end();it=nxt)
{
auto tmp=s[u].lower_bound(now-*it);
if(tmp==it) tmp++;
if(tmp==s[u].end()) {nxt=it;nxt++;continue;}
res++;
s[u].erase(tmp);
nxt=it;nxt++;
s[u].erase(it);
}
return res;
}
inline bool check(int mid){now=mid;return dfs(1,0)>=m;}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
T.add(x,y,z),T.add(y,x,z);
}
int l=0,r=1e9,ans=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d",ans);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?