7.13

7.13模拟赛

T1 sign

签到题。。。

只会暴力dfs...本能骗到70分

出题人血坑。。。

先读入边权

最后惨遭爆零

先附上70代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=10005;
int n,m=0,in[N],w[N],st[N];
vector< pair<int,int> >g[N<<1];
long long ans;

void dfs(int x,int fa) {
	int siz=g[x].size();
	for(int i=0;i<siz;i++) {
		int y=g[x][i].first;
		if(y==fa)continue;
		w[y]+=w[x]+g[x][i].second;
		ans+=w[y];
		dfs(y,x);
	}
}
int main() {
	freopen("sign.in","r",stdin);
	freopen("sign.out","w",stdout);
	n=read();
	for(int i=1,x,y,z;i<n;i++) {
		x=read();y=read();z=read();
		g[x].push_back(make_pair(y,z));
		g[y].push_back(make_pair(x,z));
		in[x]++;in[y]++;
	}
	for(int i=1;i<=n;i++)
		if(in[i]==1)
			st[++m]=i;
	int cnt=0;
	for(int i=1;i<=m;i++) {
		dfs(st[i],0);
		memset(w,0,sizeof(w));
	}
	printf("%lld\n",ans);
	return 0;
}

正解——

按边考虑,记录其子树的叶子结点sonleaf [ ] ,然后记录子树大小siz[ ] ,dfs遍历一遍统计所有siz和sonleaf

ans=(外面的点son_leaf+里面的点(cnt_sonleaf - sonleaf ) )*边权

#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN=500005;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n,hd[MAXN];
struct node {
	int from,to,w,nxt;
}e[MAXN];
int tot;
void add(int fr,int to,int val) {
	e[++tot].from=fr;
	e[tot].to=to;
	e[tot].w=val;
	e[tot].nxt=hd[fr];
	hd[fr]=tot;
}
int root;
int du[MAXN];
bool ch[MAXN];
int cnt_leaf;
int siz[MAXN],son_leaf[MAXN];
int to_fa[MAXN];
void dfs(int x,int fa){
	siz[x]++;
	for(int i=hd[x];i;i=e[i].nxt) {
		if(e[i].to==fa)	{
			to_fa[x]=i;
			continue;
		}
		dfs(e[i].to,x);
		siz[x]+=siz[e[i].to];
		son_leaf[x]+=son_leaf[e[i].to];
	}
}
long long ans;
int main() {
	n=read();
	for(int i=1,u,v,w;i<=n-1;i++) {
		w=read();u=read();v=read();
		add(u,v,w),add(v,u,w);
		du[u]++,du[v]++;
	}
	for(int i=1;i<=n;i++) {
		if(du[i]==1) {
			cnt_leaf++;son_leaf[i]=1;
		}
		else root=i;
	}
	dfs(root,0);
	for(int i=1;i<=n;i++) {
		if(i==root) continue;
		long long cnt=0;
		cnt+=(son_leaf[i]*(n-siz[i]));
		cnt+=((cnt_leaf-son_leaf[i])*siz[i]);
		ans+=cnt*e[to_fa[i]].w;
	}
	printf("%lld\n",ans);
	return 0;
}


T2map

Description

一张n个点的无向完全图,从1号点出发,每秒随机沿当前点的出度移动一条边。q次询问,每次给出一个数t[i],询问第t[i]秒在1号点的概率,答案对998244353取模。

法一:大力推式子——找规律

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
inline LL read() {
	LL x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const LL p = 998244353;
LL n,m,t;
LL pow(LL a,LL b) {
	LL ans=1;a=(a+p)%p;
	while(b) {
		if(b&1) ans=(ans*a)%p;
		a=(a*a)%p;
		b>>=1;
	}
	return ans%p;
}
LL inv(LL x) {
	return pow(x,p-2);
}
int main() {
	n=read()-1;m=read();
	while(m--) {
		t=read();
		if(t==0) {
			puts("1");continue; 
		} else if(t==1) {
			puts("0");continue; 
		} else {
			LL c=inv((pow(n,t)+pow(n,t-1))%p);
			if(t&1) {
				printf("%lld\n",(pow(n,t-1)-1+p)%p*c%p);	
			} else {
				printf("%lld\n",(pow(n,t-1)+1)%p*c%p);	
			}
		}
	}
	return 0;
}

法二:构造矩阵,矩阵快速幂

\[分为1号点和非1号点考虑\\ 设f[i][0]表示前i秒在所有非1号点的期望,f[i][1]表示前i秒在1号点的期望\\ f[i][0]=f[i-1][1]*1+f[i-1][0]*{({n-2)}/(n-1)}\\ f[i][1]=f[i-1][0]*1/(n-1)\\ \]

#include <cstdio>
#include <iostream>
#include <cstring>
typedef long long ll;
inline ll read() {
	ll x=0;int f=1;char ch=getchar();
	while(!isdigit(ch)){ch=='-'&&(f=-1);ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
using namespace std;
const int P = 998244353;
inline void ADD(ll &a, ll b) {
	a += b;
	if (a >= P)	a -= P;
}
struct matrix{
	int n, m;
	ll h[5][5];
	matrix() {
		n = m = 0;
		memset(h, 0, sizeof(h));
	}
	matrix operator *(const matrix a) const {
		matrix mt;
		mt.n = n, mt.m = m;
		for (register int k = 1; k <= m; ++k) {
			for (register int i = 1; i <= n; ++i) {
				for (register int j = 1; j <= m; ++j) {
					ADD(mt.h[i][j], h[i][k] * a.h[k][j] % P);
				}
			}
		}
		return mt;
	}
};
inline ll quickpow(ll x, ll k) {
	x %= P;
	ll res = 1;
	while (k) {
		if (k & 1)	res = res * x % P;
		x = x * x % P;
		k >>= 1;
	}
	return res;
}

inline matrix Qp(matrix x, ll k) {
	matrix res = x; --k;
	while (k) {
		if (k & 1)	res = res * x;
		x = x * x;
		k >>= 1;
	}
	return res;
}

ll n, q;
int main() {
    n=read();q=read();
	matrix A, B;
	A.n = 1, A.m = B.n = B.m = 2;
	A.h[1][1] = 1;
	B.h[1][2] = 1;
	B.h[2][1] = quickpow(n - 1, P - 2);
	B.h[2][2] = (n - 2) % P * quickpow(n - 1, P - 2) % P;
	for (register int i = 1; i <= q; ++i) {
		ll t; t=read();
		if (t == 0) {
			puts("1");
			continue;
		}
		matrix C = A * Qp(B, t);
		printf("%lld\n", C.h[1][1]);
	}
	return 0;
}

T3path

好吧,我还是只会暴力。。。菜炸了55分

spfa 最短路。。。刘队说这稀疏图spfa肯定挂了,啊好像dij 能多骗5分。。。算了

说正解吧——还有俩正解

线段树优化建图

适用情况

当点数很多,并且边集以区间形式 (点-->区间/区间->点) 给出

强行连边复杂度太大,考虑如何维护同一个子区间的连边,使用线段树优化。

优化思路

先考虑 点-->区间 连边的部分。

考虑建图的复杂度真正体现在哪里。

如果所有的被连边区间不交,那么总复杂度是对的。

所以真正建图的复杂度,产生在被连边区间相交的部分,尤其是一个同一个区间被多个点连边,那么我们每一个都要产生区间长度的代价。

那么有一个思路,如果有一个区间被多次连边,那么不妨我们建立这个区间的一个代表节点。

首先预处理的时候将这个代表节点(虚点)连向区间里的每一个节点,边权为 0,就代表了 连向代表节点的每一条边 都连向了区间里的每一个节点,然后都每次都将边连向这个代表节点即可。

建图过程 (点 -->区间)

但是注意到这种区间并不是很好找,这种找区间的方法并不是具有很强的普适性。

我们知道线段树的每一个节点维护的是一个固定的区间,线段树的区间操作是 log 的。

回忆区间加的过程,如果一个节点的代表区间被完全覆盖,那么我们会在这个节点上打标记,等到需要的时候再下传。这体现了一种代表的关系,即区间共有的性质我可以只标记在代表这个区间的节点上。

如果要从一个点连向一个区间,那么可以像区间加那样,把这个点向完全覆盖的区间连边。

也就是说,在区间连边时,在线段树上每找到一个完全覆盖的节点,就将出发点向这个区间的代表节点连边。


同样的,我们先考虑预处理。

一个节点被连边,代表这个节点的 子树 都可以到达。

之所以强调子树,而不是强调区间里的每一个节点,因为我们预处理的边只会向下 连一层

只连下一层的边数量级是 O(2N) ,而连向区间里的每一个节点显然每一层都是O(N) ,总的边的数量级就是 $O(Nlog_2N) $,边的级别显然大了很多,并且也不符合线段树的关系定义。

一个8个节点的线段树预处理之后的连边情况如图所示。图中边权全部为 0。

img

然后一条连边就可以按照我们说的那样了,找到对应的区间就好。

假如8 号点要向区间 [2,5] 连边,最后会是蓝边的样子(边权就省略了):

img

建图过程 (区间 -->点)

发现原来线段树的区间传递关系并不对了。

如果我们找到了每一段区间,直接向指向的点连边,子树内真正建边的点并没有连出这一条边。

如图,如果要区间[5,8]向T 连边,蓝框框起来的点并没有真正向T 连边。

因为建树的时候绿色边的方向是向区间内传递的。

img

很容易想到反向再建一棵树。显然叶节点是相同的,所以两棵树会共用叶节点。

img

倒着的树上传递关系就变为,一个节点连了出边,那么他所代表的区间里所有节点都向外连了一条出边。

同理,这些绿色的边的边权都是0 。

关于正确性,显然更大的区间并不会到达小区间,所以连边不用担心范围超界。

此时我们就可以在倒着的树上找所有的区间,然后连出边就可以了。

总结:

1.建立两个线段树,一个是入树,一个是出树(动态开点)

2.连边:

建立两个虚点,虚点之间边权不为0

从入树找到出发区间对应的节点,从这些节点向其中一个虚点连接边权为0的边,从出树找到到达区间对应的节点,从另一个虚点向对应的节点连接边权为0的边

image-20200716161632099

Solution

我们对每一条信息都单独开出来一个转移节点,然后就变成了 区间--> 点和 点-->区间的问题了。

但是双向边不能共用一个转移节点。因为这样其实上在 $ [l_1,r_1] $内的每个点之间都连了一条长度为2 的边。

实际做的时候,对每一个单向边都开一个转移节点,然后出边边权都设为 0,入边边权都设为 1就可以了。

还有一个比较妙的方法,是出入边权都设为1 然后最短路的答案除以2 ,也是可以的。

最短路不用 dij ,双端bfs即可 (0扔前面,1扔后面)

my代码:

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
inline int read() {
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f; 
}
const int N=12000010;
int n,m,s,rt_in,rt_out,tot;
int ls[N],rs[N];
int in[N],out[N];
int hd[N],e_cnt;
struct node{
    int id,val;
    node(){}
    node(int a,int b):id(a),val(b){}
};
struct edge{
    int to,nxt,w;
    edge(){}
    edge(int a,int b,int c):to(a),nxt(b),w(c){}
}e[N];
inline void add(int x,int y,int z){
    e[++e_cnt]={y,hd[x],z};hd[x]=e_cnt;
}
inline void build_in(int l,int r,int &p){
    p=++tot;
    if(l==r){in[l]=p;return;}
    int mid=(l+r)>>1;
    build_in(l,mid,ls[p]);
    build_in(mid+1,r,rs[p]);
    add(ls[p],p,0);
    add(rs[p],p,0);
}
inline void build_out(int l,int r,int &p){
    p=++tot;
    if(l==r){out[l]=p;return;}
    int mid=(l+r)>>1;
    build_out(l,mid,ls[p]);
    build_out(mid+1,r,rs[p]);
    add(p,ls[p],0);
    add(p,rs[p],0);
}
inline void insert_in(int ql,int qr,int l,int r,int cur,int p){
    if(ql<=l&&r<=qr){add(p,cur,1);return;}
    int mid=(l+r)>>1;
    if(ql<=mid) insert_in(ql,qr,l,mid,cur,ls[p]);
    if(qr>mid) insert_in(ql,qr,mid+1,r,cur,rs[p]);
}
inline void insert_out(int ql,int qr,int l,int r,int cur,int p){
    if(ql<=l&&r<=qr){add(cur,p,1);return;}
    int mid=(l+r)>>1;
    if(ql<=mid) insert_out(ql,qr,l,mid,cur,ls[p]);
    if(qr>mid) insert_out(ql,qr,mid+1,r,cur,rs[p]);
}
int dis[N];
bool vis[N];
void work(){
    deque <node> q;
    memset(dis,0x3f,sizeof(dis));
    dis[in[s]]=0;q.push_front({in[s],0});
    while(!q.empty()){
        int x=q.front().id;q.pop_front();
        if(vis[x])continue;
        vis[x]=1;
        for(int i=hd[x];i;i=e[i].nxt){
            int y=e[i].to,z=e[i].w;
            if(dis[y]>dis[x]+z){
                dis[y]=dis[x]+z;
                if(!vis[y]){
                    if(!z) q.push_front({y,dis[y]});
                    else q.push_back({y,dis[y]});
                }
            }
        }
    }
}
int main(){
    n=read();m=read();s=read();
    build_in(1,n,rt_in);
    build_out(1,n,rt_out);
    for(int i=1;i<=n;i++) add(in[i],out[i],0),add(out[i],in[i],0);
    for(int i=1;i<=m;i++){
        int l1=read(),r1=read(),l2=read(),r2=read();
        tot++;insert_in(l1,r1,1,n,tot,rt_in),insert_out(l2,r2,1,n,tot,rt_out);
        tot++;insert_in(l2,r2,1,n,tot,rt_in),insert_out(l1,r1,1,n,tot,rt_out);
    }
    work();
    for(int i=1;i<=n;i++)
        printf("%d\n",dis[out[i]]/2);
    return 0;
}

hs_black更优秀做法https://www.luogu.com.cn/blog/hs-black/solution-p6348

老师

std:

#include<bits/stdc++.h>
#define N 1000010
#define M 4000010
#define Rg register
#define mid ((l + r) >> 1)
using namespace std;

inline int rd() {
    int x = 0;
    bool f = 0;
    char c = getchar();
    while (!isdigit(c)) {
        if (c == '-') f = 1;
        c = getchar();
    }
    while (isdigit(c)) {
        x = x * 10 + (c ^ 48);
        c = getchar();
    }
    return f ? -x : x;
}

int n, m, s, cnt, tot, hd[N << 3];

struct edge{
    int w, to, nxt;
} e[M << 1];

inline void add(int u, int v, int w) {
  e[++tot].to = v; 
  e[tot].w = w;
  e[tot].nxt = hd[u]; 
  hd[u] = tot;
}

struct segmentin{

  int root, ptr;

  inline int newnode(){return ++ptr;}

  struct node{int ls, rs, p;}c[N << 1];

  void build(int &rt, int l, int r) {
    rt = newnode();
    if(l == r) {
        c[rt].p = l;
        return;
    }
    build(c[rt].ls, l, mid);
    build(c[rt].rs, mid + 1, r);
    c[rt].p = ++cnt;
    add(c[rt].p, c[c[rt].ls].p, 0);
    add(c[rt].p, c[c[rt].rs].p, 0);
  }

  void updata(int rt, int l, int r, int L, int R, int p, int w) {
    if(l > R || r < L) return;
    if(l >= L && r <= R) {
        add(p, c[rt].p, w);
        return;
    }
    if(L <= mid) updata(c[rt].ls, l, mid, L, R, p, w);
    if(R > mid) updata(c[rt].rs, mid+1, r, L, R, p, w);
  }

}treein;

struct segmentout{

  int root, ptr;

  inline int newnode(){return ++ptr;}

  struct node{int ls, rs, p;}c[N << 1];

  void build(int &rt, int l, int r) {
    rt = newnode();
    if(l == r) {
        c[rt].p = l;
        return;
    }
    build(c[rt].ls, l, mid);
    build(c[rt].rs, mid + 1, r);
    c[rt].p = ++cnt;
    add(c[c[rt].ls].p, c[rt].p, 0);
    add(c[c[rt].rs].p, c[rt].p, 0);
  }

  void updata(int rt, int l, int r, int L, int R, int p, int w) {
    if(l > R || r < L) return;
    if(l >= L && r <= R) {
        add(c[rt].p, p, w);
        return;
    }
    if(L <= mid) updata(c[rt].ls, l, mid, L, R, p, w);
    if(R > mid) updata(c[rt].rs, mid + 1, r, L, R, p, w);
  }

}treeout;

int dis[N << 3];

bool vis[N << 3];

priority_queue<pair<int, int> >q;

inline void dij(){
  memset(dis, 0x3f, sizeof(dis));
  dis[s] = 0; q.push(make_pair(0, s));
  while(!q.empty()) {
    int u = q.top().second; 
    q.pop();
    if(vis[u]) continue; 
    vis[u]=1;
    for(Rg int i = hd[u], v; i; i = e[i].nxt)
      if(dis[v = e[i].to] > dis[u] + e[i].w) {
        dis[v] = dis[u] + e[i].w;
        q.push(make_pair(-dis[v], v));
      }
  }
}

int main(){
  cnt = n = rd(); 
  m = rd(); 
  s = rd();
  treein.build(treein.root, 1, n);
  treeout.build(treeout.root, 1, n);
  for(Rg int i = 1, l1, r1, l2, r2; i <= m; ++i) {
    ++cnt;
    l1 = rd(); 
    r1 = rd();
    l2 = rd();
    r2 = rd();
    treein.updata(treein.root, 1, n, l1, r1, cnt, 1);
    treeout.updata(treeout.root, 1, n, l2, r2, cnt, 1);
    ++cnt;
    treein.updata(treein.root, 1, n, l2, r2, cnt, 1);
    treeout.updata(treeout.root, 1, n, l1, r1, cnt, 1);
  }
  dij();
  for(Rg int i = 1; i <= n; ++i) 
    printf("%d\n", dis[i] / 2);
  return 0;
}
posted @ 2020-08-14 00:15  ke_xin  阅读(137)  评论(0编辑  收藏  举报