zoukankan      html  css  js  c++  java
  • 2021.9.8 模拟赛

    前言

    手残+脑残=我。

    T1 [SHOI2016]随机序列 加强版

    题目

    洛谷(弱化版)

    加强版数据有 (a_i=0) 的情况,考试没过是因为有个地方的 (-1) 打掉了,然而可以过没 (0) 的情况,所以样例没出锅。

    加强版
    //12252024832524
    #include <set>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define TT template<typename T>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 100005;
    const int MOD = 1e9 + 7;
    int n,m;
    int a[MAXN];
    
    LL Read()
    {
    	LL x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    int qpow(int x,int y)
    {
    	int ret = 1;
    	while(y){if(y & 1) ret = 1ll * ret * x % MOD;x = 1ll * x * x % MOD;y >>= 1;}
    	return ret;
    }
    
    #define lc (x<<1)
    #define rc (x<<1|1)
    int s[MAXN << 2],val[MAXN << 2],lz[MAXN << 2],PRE = 1;
    int Ad(int x){if(x >= MOD) x -= MOD; if(x < 0) x += MOD; return x;}
    void up(int x){s[x] = Ad(s[lc] + s[rc]);}
    void calc(int x,int val){s[x] = 1ll * s[x] * val % MOD;lz[x] = 1ll * lz[x] * val % MOD;}
    void down(int x)
    {
    	if(lz[x] == 1) return;
    	calc(lc,lz[x]);
    	calc(rc,lz[x]);
    	lz[x] = 1;
    }
    void Build(int x,int l,int r)
    {
    	lz[x] = 1;
    	if(l == r)
    	{
    		PRE = 1ll * PRE * a[l] % MOD;
    		if(l < n) s[x] = 2ll * PRE * qpow(3,n-l-1) % MOD;
    		else s[x] = PRE;
    		return;
    	}
    	int mid = (l+r) >> 1;
    	Build(lc,l,mid); Build(rc,mid+1,r);
    	up(x);
    }
    void Mul(int x,int l,int r,int ql,int qr,int v)
    {
    	if(ql <= l && r <= qr)
    	{
    		calc(x,v);
    		return;
    	}
    	down(x);
    	int mid = (l+r) >> 1;
    	if(ql <= mid) Mul(lc,l,mid,ql,qr,v);
    	if(mid+1 <= qr) Mul(rc,mid+1,r,ql,qr,v);
    	up(x);
    }
    int Query(int x,int l,int r,int ql,int qr)
    {
    	if(ql > qr) return 0;
    	if(ql <= l && r <= qr) return s[x];
    	down(x);
    	int mid = (l+r) >> 1,ret = 0;
    	if(ql <= mid) ret += Query(lc,l,mid,ql,qr);
    	if(mid+1 <= qr) ret += Query(rc,mid+1,r,ql,qr);
    	return Ad(ret);
    }
    
    set<int> cao;
    set<int>::iterator it;
    
    int main()
    {
    //	freopen("task.in","r",stdin);
    //	freopen("task.out","w",stdout);
    	n = Read(); m = Read();
    	for(int i = 1;i <= n;++ i) 
    	{
    		a[i] = Read();
    		if(!a[i]) a[i] = 1,cao.insert(i);
    	}
    	Build(1,1,n);
    	cao.insert(n+1);
    	for(int i = 1;i <= m;++ i)
    	{
    		int pos = Read(),v = Read();
    		if(!v) cao.insert(pos);
    		else
    		{
    			it = cao.lower_bound(pos);
    			if((*it) == pos) cao.erase(it);
    			Mul(1,1,n,pos,n,1ll * qpow(a[pos],MOD-2) * v % MOD);
    			a[pos] = v;
    		}
    		it = cao.begin();
    		Put(Query(1,1,n,1,*it-1),'
    ');//曾经,这里没有-1 
    	}
    	return 0;
    }
    

    题解

    挖掘性质后发现能加就能减,对答案没有贡献,所以只考虑前缀中没有加减的情况,即只有乘法。

    直接线段树维护一下即可,有 (0) 就相当于在中间夹断,求答案的时候不全局求。

    T2 牛客第五场A题 Portal

    题目

    牛客

    由于我看的可能是魔改过后的题目,题意可能会有区别,但是本质是一样的。

    题解

    首先我们把取货点和送货点都看成必经打卡点。

    这道题的启发,可知我们DP不需要记录当前人在哪里,因为一定在某个打卡点处。

    然后挖掘性质,发现如果我们要使用传送门,原地开一个当起点就好,所以只用记录一个传送门的位置。

    然后就可以 (O(n^2k)) DP了,数组可以滚动,但没必要,但如果不滚动记得两倍空间。

    没滚动的代码
    //12252024832524
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define TT template<typename T>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 305;
    const LL INF = 1ll << 60;
    int n,m,s;
    
    LL Read()
    {
    	LL x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    LL dis[MAXN][MAXN],dp[MAXN << 1][MAXN];//任务i,传送门在j 
    int pos[MAXN << 1];
    
    int main()
    {
    //	freopen("express.in","r",stdin);
    //	freopen("express.out","w",stdout);
    	n = Read(); m = Read(); s = Read() << 1;
    	for(int i = 1;i <= n;++ i)
    		for(int j = 1;j <= n;++ j)
    			if(i^j) dis[i][j] = INF;
    	for(int i = 1,u,v;i <= m;++ i)	
    		u = Read(),v = Read(),dis[u][v] = dis[v][u] = Min(dis[u][v],Read());
    	for(int k = 1;k <= n;++ k)
    		for(int i = 1;i <= n;++ i)
    			for(int j = 1;j <= n;++ j)
    				dis[i][j] = Min(dis[i][j],dis[i][k]+dis[k][j]);
    	for(int i = 1;i <= s;++ i) pos[i] = Read();
    	memset(dp,0x3f,sizeof(dp));
    	for(int i = 1;i <= n;++ i) //枚举传送门 
    		for(int j = 1;j <= n;++ j)//中转传送门 
    			dp[1][i] = Min(dp[1][i],dis[1][j]+dis[i][j]+Min(dis[i][pos[1]],dis[j][pos[1]]));
    	for(int i = 2;i <= s;++ i)
    		for(int j = 1;j <= n;++ j)
    		{
    			dp[i][j] = Min(dp[i][j],dp[i-1][j]+dis[pos[i-1]][pos[i]]);
    			for(int k = 1;k <= n;++ k)
    				dp[i][k] = Min(dp[i][k],dp[i-1][j]+dis[j][k]+Min(dis[k][pos[i]],dis[j][pos[i]])),//通过传送门去放传送门 
    				dp[i][k] = Min(dp[i][k],dp[i-1][j]+dis[pos[i-1]][k]+Min(dis[k][pos[i]],dis[j][pos[i]]));//走去放传送门,再通过传送门到当前点 
    		}
    	LL ans = INF;
    	for(int i = 1;i <= n;++ i) ans = Min(ans,dp[s][i]);
    	Put(ans);
    	return 0;
    }
    

    T3 牛客第五场B题 Graph

    题目

    牛客

    此条目同上。

    题解

    挖掘性质后发现其实就是最小异或生成树,然后我不会,只能糊一个 ( t Prim) 上去拿了个 (O(n^2))(70pts)

    正解是改进的 ( t Boruvka),每轮操作可以不用 (O(m)) 枚举,而是 (O(nlog_2a_i)) 在字典树或者权值线段树上查询。

    由于要动态维护连通性,还要询问异或最小,所以用并查集+线段树合并,用主席树查询的思想做就行了。

    还有一个常数小和代码短的做法是权值分治,考虑分 (log_2a_i) 层,每层按当前层分为 (01) 两部分,显然层级越高我们希望连的边越少,所以让左右子树分别连通之后左右 (01) 两部分只有连 (1) 条边即可,至于连的边嘛,当然是一半插入 Trie ,另一半查询咯。

    两个做法时间复杂度是一样的,都是 (O(nlog_2nlog_2a_i))

    线段树合并大常数做法
    //12252024832524
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define TT template<typename T>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 200005;
    const int INF = 1 << 30;
    int n;
    
    LL Read()
    {
    	LL x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    int head[MAXN],etot;
    struct edge
    {
    	int v,w,nxt;
    }e[MAXN << 1];
    void Add_Edge(int x,int y,int z)
    {
    	e[++etot].v = y;
    	e[etot].w = z;
    	e[etot].nxt = head[x];
    	head[x] = etot;
    }
    void Add_Double_Edge(int x,int y,int z)
    {
    	Add_Edge(x,y,z);
    	Add_Edge(y,x,z);
    }
    int d[MAXN],val[MAXN];
    void dfs(int x)
    {
    	for(int i = head[x]; i ;i = e[i].nxt)
    		if(!d[e[i].v])
    			d[e[i].v] = d[x] + 1,val[e[i].v] = val[x] ^ e[i].w,dfs(e[i].v);
    }
    bool vis[MAXN];
    int od[MAXN],f[MAXN];
    bool cmp(int x,int y){return val[x] < val[y];}
    int findSet(int x)
    {
    	if(f[x]^x) f[x] = findSet(f[x]);
    	return f[x];
    }
    
    int rt[MAXN],tot,who;
    int ch[MAXN*60][2],siz[MAXN*60],ID[MAXN*60];
    void Add(int &x,int v,int rk)
    {
    	if(!x) x = ++tot;
    	++siz[x];
    	if(rk < 0) {ID[x] = who;return;}
    	bool to = v >> rk & 1;
    	Add(ch[x][to],v,rk-1);
    }
    int mge(int x,int y)
    {
    	if(!x || !y) return x|y;
    	siz[x] += siz[y];
    	ch[x][0] = mge(ch[x][0],ch[y][0]);
    	ch[x][1] = mge(ch[x][1],ch[y][1]);
    	return x;
    }
    void unionSet(int u,int v)
    {
    	u = findSet(u); v = findSet(v);
    	if(u^v) f[v] = u,rt[u] = mge(rt[u],rt[v]);
    }
    int MIN[MAXN],CID[MAXN],ori;
    void Query(int lst,int x,int v,int rk)
    {
    	if(v >= MIN[who]) return;
    	if(rk < 0)
    	{
    		CID[who] = ID[lst];
    		MIN[who] = v;
    		return;
    	}
    	bool to = val[ori] >> rk & 1;
    	if(siz[ch[lst][to]] - siz[ch[x][to]]) Query(ch[lst][to],ch[x][to],v,rk-1);
    	else Query(ch[lst][to^1],ch[x][to^1],v|(1<<rk),rk-1);
    }
    
    int main()
    {
    //	freopen("xor.in","r",stdin);
    //	freopen("xor.out","w",stdout);
    	n = Read();
    	for(int i = 1,u,v;i < n;++ i)
    	{
    		u = Read()+1,v = Read()+1;
    		Add_Double_Edge(u,v,Read());
    	}
    	d[1] = 1,dfs(1);
    	for(int i = 1;i <= n;++ i) val[i] = Read(); 
    	for(int i = 1;i <= n;++ i) od[i] = f[i] = i,MIN[i] = INF;
    	int cnt = n;
    	sort(od+1,od+n+1,cmp);
    	for(int i = 2;i <= n;++ i)
    		if(val[od[i]] == val[od[i-1]])
    			unionSet(od[i-1],od[i]),--cnt;
    	for(int i = 1;i <= n;++ i)
    		if(findSet(i) == i)
    			who = i,Add(rt[i],val[i],29),Add(rt[0],val[i],29);
    	LL ans = 0;
    	while(cnt > 1)
    	{
    		for(int i = 1;i <= n;++ i)
    		{
    			who = findSet(i); ori = i;
    			Query(rt[0],rt[who],0,29);
    		}
    		for(int i = 1;i <= n;++ i)
    		{
    			if(MIN[i] < INF && findSet(i) != findSet(CID[i]))
    			{
    				unionSet(i,CID[i]);
    				ans += MIN[i];
    				MIN[i] = INF;
    				--cnt;
    			}
    		}
    	}
    	Put(ans);
    	return 0;
    }
    
  • 相关阅读:
    《Effective Java》第9章 异常
    《Effective Java》第7章 方法
    《Effective Java》第6章 枚举和注解
    《Effective Java》第5章 泛型
    《Effective Java》第4章 类和接口
    《Effective Java》第3章 对于所有对象都通用的方法
    使用Spring加载properties配置文件.md
    第7章 插件的使用和写法
    第6章 jQuery与Ajax的应用
    第5章 jQuery对表单、表格的操作及更多应用
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15244705.html
Copyright © 2011-2022 走看看