CSP模拟27
考的有一点意外,出乎意料。
[CF1060E] Sergey and Subway
考场上打假了,乐。
设 \(dis_{i,j}\) 表示 \(i\) 和 \(j\) 的树上距离。
很容易发现,答案其实就是:
其实就是所有点对的距离,加上距离为奇数的点对的个数,最后除以二就可以了。
复杂度 \(O(n)\)
点击查看代码
#include <iostream>
#include <cstdio>
#include <cmath>
#define int long long
const int MAXN=2e5+10;
using namespace std;
inline int read() {
int f=1,x=0;
char ch=getchar();
while(ch>'9' || ch<'0') {
if(ch=='-') {
f=-1;
}
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return f*x;
}
int n,cnt,ans,tot,s0,s1;
int dep[MAXN],siz[MAXN],fa[MAXN];
struct edge {
int to,next;
}a[MAXN<<1];
int head[MAXN];
void add(int u,int v) {
a[++cnt].to=v;
a[cnt].next=head[u];
head[u]=cnt;
}
int g(int x) {
if(x&1) return (x+1)/2;
return x/2;
}
void dfs(int now,int father) {
dep[now]=dep[father]+1;
siz[now]=1;
if(dep[now]&1) s1++;
else s0++;
for(int i=head[now];i;i=a[i].next) {
int v=a[i].to;
if(v==father) continue;
dfs(v,now);
siz[now]+=siz[v];
}
}
void dfs1(int now,int father) {
for(int i=head[now];i;i=a[i].next) {
int v=a[i].to;
if(v==father) continue;
tot+=(siz[v]*(n-siz[v]));
dfs1(v,now);
}
}
signed main() {
n=read();
for(int i=1;i<n;i++) {
int u=read(),v=read();
add(u,v);
add(v,u);
}
dfs(1,0);
int sum=0;
for(int i=1;i<=n;i++) {
if(dep[i]&1) {
sum+=s0;
}
else {
sum+=s1;
}
}
sum/=2;
dfs1(1,0);
printf("%lld",(tot+sum)/2);
return 0;
}
/*
4
1 2
1 3
1 4
*/
[CF979D] Kuro and GCD and XOR and SUM
可以先考虑暴力,拿下10分。
观察数据范围,发现值域比较的小。
先考虑第二个条件,$ k|\gcd(v,x) $ ,在这里其实就是 \(k|v\) 且 \(k|x\) 的意思,
考虑第三个条件,异或最大值经典做法是 01-tire ,这里只不过加了几个限制。
对于第二个限制,我们可以在加入元素时,根据元素的约数(可以预处理出来)各建一个 Tire ,在每个约数的 Tire 内插入这个元素,查询 \(k\) 时直接在 \(k\) 的那个 Tire 里找就可以。
对于第一个限制,我们考虑对于 Trie 的每个节点设一个 \(mi\) 值,表示的是经过这个节点的数的最小值,如果 \(mi<k\) ,就不可以往这里走,换另一边。
复杂度 ,时间复杂度为 \(O(x\log{x})\) ,空间复杂度为 \(O(x\log{x})\)
点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#define int long long
const int MAXN=1e7;
const int M=1e5+10;
const int inf=1e18;
using namespace std;
inline int read() {
int f=1,x=0;
char ch=getchar();
while(ch>'9' || ch<'0') {
if(ch=='-') {
f=-1;
}
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return f*x;
}
int q,cnt;
struct Tire {
int son[2];
}tr[MAXN];
int mi[MAXN],rt[M];
bool vis[M],a[M];
vector <int> y[MAXN];
void init() {
for(int i=1;i<=1e5;i++) {
for(int j=i;j<=1e5;j+=i) {
y[j].push_back(i);
}
}
}
void insert(int root,int x) {
int now=root;
mi[root]=min(mi[root],x);
for(int i=31;i>=0;i--) {
int ch=((x>>i)&1);
if(!tr[now].son[ch]) {
tr[now].son[ch]=++cnt;
}
now=tr[now].son[ch];
mi[now]=min(mi[now],x);
}
}
int query(int k,int x,int s) {
int now=rt[k];
if(x%k!=0 || mi[now]+x>s) {
return -1;
}
int ans=0;
for(int i=31;i>=0;i--) {
int ch=((x>>i)&1);
if(tr[now].son[!ch] && mi[tr[now].son[!ch]]+x<=s) {
ans+=((ch^1)<<i);
now=tr[now].son[!ch];
}
else {
ans+=(ch<<i);
now=tr[now].son[ch];
}
}
return ans;
}
signed main() {
init();
for(int i=0;i<=1e7;i++) {
mi[i]=inf;
}
q=read();
for(int g=1;g<=q;g++) {
int op=read();
if(op==1) {
int x=read();
if(a[x]) continue;
a[x]=1;
for(int i=0;i<y[x].size();i++) {
int now=y[x][i];
if(!vis[now]) {
rt[now]=++cnt;
vis[now]=1;
}
insert(rt[now],x);
}
}
if(op==2) {
int x=read(),k=read(),s=read();
printf("%lld\n",query(k,x,s));
}
}
return 0;
}
/*
5
1 1
1 2
2 1 1 3
2 1 1 2
2 1 1 1
*/
/*
2
1
-1
*/
[CF1101F] Trucks and Cities
直接暴力二分,再加上一些神奇的优化,就可以跑的飞快,非常的nice,在OJ上拿下最优解 (但不是正解)
复杂度 \(O(能过)\)
点击查看代码
#include <iostream>
#include <cstdio>
#define int long long
const int MAXN=410;
const int inf=2e18;
using namespace std;
inline int read() {
int f=1,x=0;
char ch=getchar();
while(ch>'9' || ch<'0') {
if(ch=='-') {
f=-1;
}
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return f*x;
}
int n,m,s,t,c,r,ans;
int a[MAXN];
bool check(int x) {
int rr=r,now=x;
bool flag=1;
for(int i=s;i<t;i++) {
int sp=(a[i+1]-a[i])*c;
if(sp>now) {
if(rr==0) return 0;
now=x;
rr--;
if(now<sp) {
return 0;
}
}
now-=sp;
}
return 1;
}
signed main() {
//freopen("drive.in","r",stdin);
// freopen("drive.out","w",stdout);
n=read() ,m=read();
for(int i=1;i<=n;i++) {
a[i]=read();
}
for(int i=1;i<=m;i++) {
s=read() ,t=read() ,c=read() ,r=read();
int z=ans,y=inf,sum=0;
if(check(ans)) continue;
while(z<=y) {
int mid=(z+y)>>1;
if(check(mid)) {
sum=mid;
y=mid-1;
}
else {
z=mid+1;
}
}
ans=max(sum,ans);
}
printf("%lld\n",ans);
return 0;
}
/*
7 6
2 5 7 10 14 15 17
1 3 10 0
1 7 12 7
4 5 13 3
4 7 10 1
4 7 10 1
1 5 11 2
*/
[JOISC2022] Jail
思路比较的简单,对于先走和后走,我们可以得出一个结论:
如果存在方案,一定有一种方案使每个人都是从起点不停歇地走到终点。
简单证明一下,如果 \(A\) 为了让 \(B\) 能够顺利通过而让路,从而使 \(B\) 先到终点,那么 \(A\) 可以先走到终点,然后 \(B\) 再走,多个点也是一样的。
那么我们很容易得出两条结论:
1.如果 A 的起点在 B 的路径上,那么 A 一定比 B 先走。
2.如果 A 的终点在 B 的路径上,那么 B 一定比 A 先走。
根据这个关系,可以建出一张有向图,暴力跳路径,然后加边,再跑一遍拓扑来判断是否有环。
但是这样暴跳复杂度为 \(O(T(m(n+\log n)))\) ,不可过.
考虑优化建边,很容易想到 树链剖分 和 线段树优化建边。
我们设 \(u_i\) 和 \(v_i\) 分别表示 第 \(i\) 条路径的起点与终点。
先处理起点,对于起点,我们想把路径上的所有除自己之外的起点和路径连边。
首先我们把 \(i\) 向 \(u_i\) 连边,再把路径 在线段树上对应的区间 在 内向树上 和 \(i\) 连边,这样连边我们会让路径上所有的起点向 该路径的起点连边 ,但是这样连边天然成环,因为这条路径的起点经过线段树和路径的编号连向了自己。那么我们在连边时,把路径起点排除掉就可以了。
对于终点思路完全相同,只不过所有边的方向反过来(包括线段树内边)。
复杂度 \(O(Tm \log n)\)
点击查看代码
#include <iostream>
#include <cstdio>
#include <queue>
#define int long long
const int MAXN=5e6+10;
using namespace std;
inline int read() {
int f=1,x=0;
char ch=getchar();
while(ch>'9' || ch<'0') {
if(ch=='-') {
f=-1;
}
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return f*x;
}
int T,n,m,cnt,cnt1,tim;
struct edge {
int next,to;
}a[MAXN<<1],e[MAXN<<1];
int head[MAXN],head1[MAXN],vis[MAXN];
int dep[MAXN],siz[MAXN],son[MAXN],fa[MAXN];
int top[MAXN],id[MAXN],mp[MAXN],in[MAXN];
void init() {
for(int i=1;i<=n;i++) {
dep[i]=siz[i]=fa[i]=son[i]=0;
id[i]=top[i]=head[i]=0;
}
for(int i=1;i<=cnt;i++) {
a[i].to=a[i].next=0;
}
for(int i=1;i<=cnt1;i++) {
e[i].to=e[i].next=0;
}
for(int i=1;i<=n*8+m;i++) {
head1[i]=0;
in[i]=mp[i]=0;
}
cnt=0,n=0,m=0,cnt1=0,tim=0;
}
void add(int u,int v) {
a[++cnt].to=v;
a[cnt].next=head[u];
head[u]=cnt;
}
void add1(int u,int v) {
e[++cnt1].to=v;
e[cnt1].next=head1[u];
head1[u]=cnt1;
in[v]++;
}
void dfs1(int now,int father) {
dep[now]=dep[father]+1;
fa[now]=father;
siz[now]=1;
int maxson=-1;
for(int i=head[now];i;i=a[i].next) {
int v=a[i].to;
if(v==father) continue;
dfs1(v,now);
siz[now]+=siz[v];
if(siz[v]>maxson) {
maxson=siz[v];
son[now]=v;
}
}
}
void dfs2(int now,int topf) {
top[now]=topf;
id[now]=++tim;
if(son[now]) dfs2(son[now],topf);
for(int i=head[now];i;i=a[i].next) {
int v=a[i].to;
if(v==fa[now] || v==son[now]) continue;
dfs2(v,v);
}
}
void build(int k,int l,int r) {
if(l==r) {
mp[l]=k;
return;
}
add1(k,k<<1) ,add1(k,k<<1|1);
add1((k<<1)+4*n,k+4*n) ,add1((k<<1|1)+4*n,k+4*n);
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
void up_date1(int k,int l,int r,int L,int R,int pos) {//外向树
if(L<=l && r<=R) {
add1(pos,k);
return;
}
int mid=(l+r)>>1;
if(L<=mid) up_date1(k<<1,l,mid,L,R,pos);
if(R>mid) up_date1(k<<1|1,mid+1,r,L,R,pos);
}
void up_date2(int k,int l,int r,int L,int R,int pos) {//内向树
if(L<=l && r<=R) {
add1(k+4*n,pos);
return;
}
int mid=(l+r)>>1;
if(L<=mid) up_date2(k<<1,l,mid,L,R,pos);
if(R>mid) up_date2(k<<1|1,mid+1,r,L,R,pos);
}
void change(int u,int v,int pos) {
add1(mp[id[u]],8*n+pos);
add1(8*n+pos,mp[id[v]]+4*n);
int x=u,y=v;
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) swap(x,y);
if(x!=u) up_date1(1,1,n,id[top[x]],id[x],8*n+pos);
else if(top[x]!=x) up_date1(1,1,n,id[top[x]],id[x]-1,8*n+pos);
if(x!=v) up_date2(1,1,n,id[top[x]],id[x],8*n+pos);
else if(top[x]!=x) up_date2(1,1,n,id[top[x]],id[x]-1,8*n+pos);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
if(x!=u && y!=u) up_date1(1,1,n,id[x],id[y],8*n+pos);
else if(x!=y) {
if(x==u) up_date1(1,1,n,id[x]+1,id[y],8*n+pos);
else up_date1(1,1,n,id[x],id[y]-1,8*n+pos);
}
if(x!=v && y!=v) up_date2(1,1,n,id[x],id[y],8*n+pos);
else if(x!=y) {
if(x==v) up_date2(1,1,n,id[x]+1,id[y],8*n+pos);
else up_date2(1,1,n,id[x],id[y]-1,8*n+pos);
}
}
queue <int> q;
signed main() {
freopen("prison.in","r",stdin);
freopen("prison.out","w",stdout);
T=read();
while(T--) {
init();
n=read();
for(int i=1;i<n;i++) {
int u=read(),v=read();
add(u,v);
add(v,u);
}
dfs1(1,0);
dfs2(1,1);
m=read();
build(1,1,n);
for(int i=1;i<=m;i++) {
int u=read(),v=read();
change(u,v,i);
}
for(int i=1;i<=8*n+m;i++) {
if(!in[i]) {
q.push(i);
}
}
while(!q.empty()) {
int now=q.front();
q.pop();
for(int i=head1[now];i;i=e[i].next) {
int v=e[i].to;
in[v]--;
if(in[v]==0) {
q.push(v);
}
}
}
bool flag=1;
for(int i=1;i<=8*n+m;i++) {
if(in[i]) {
flag=0;
break;
}
}
if(flag==1) printf("Yes\n");
else printf("No\n");
}
return 0;
}