20230704测试

H

新外号:4log

这题就有点一眼了,gcd 最多只会减少 log 次,那就直接每个节点开一个 vector ,维护前缀和后缀的整体 gcd 最多只有 log 个,那就暴力合并,加上一个求 gcdlog 一共只有 4log

好吧,其实可以增量构造,维护一个双指针,由于后面的 gcd 是单调递减的,所以可以去掉两只 log

这里很懒,用了4log 的实现

const int maxN=1e5+10;
inline int gcd(int x,int y){return (!y)?x:gcd(y,x%y);}
int idx=1,n,m,root=1;
struct node {
	vector<pii> lgc,rgc;
	ll res=0;
}seg[maxN<<1];
int son[maxN<<1][2];
int a[maxN];

node pushup(node x,node y){
	ll res=0;
	for(pii z:x.rgc)
		for(pii w:y.lgc)
			if(gcd(z.fir,w.fir)!=1)
				res+=z.sec*w.sec;
	res+=x.res+y.res;
	int last=y.rgc.back().first;
	for(pii z:x.rgc){
		int g=gcd(last,z.fir);
		if(g==last) y.rgc.back().sec+=z.sec;
		else{
			last=g;
			y.rgc.push_back({g,z.sec});
		}
	}
	last=x.lgc.back().first;
	for(pii z:y.lgc){
		int g=gcd(last,z.fir);
		if(g==last) x.lgc.back().sec+=z.sec;
		else {
			last=g;
			x.lgc.push_back({g,z.sec});
		}
	} 
	node temp;
	temp.lgc=x.lgc;
	temp.rgc=y.rgc;
	temp.res=res;
	return temp;
}

void Clear(int now){
	seg[now].rgc.clear();
	seg[now].lgc.clear();
	seg[now].res=0;
}

inline void build(int &now,int l,int r){
	if(!now) now=++idx;
	if(l==r){
		seg[now].lgc.push_back({a[l],1});
		seg[now].rgc.push_back({a[l],1});
		if(a[l]!=1) seg[now].res=1;
		return ;
	}
	int mid=(l+r)>>1;
	build(son[now][0],l,mid);
	build(son[now][1],mid+1,r);
	seg[now]=pushup(seg[son[now][0]],seg[son[now][1]]);
}

inline void modify(int now,int l,int r,int wh){
	if(l==r){
		Clear(now);
		seg[now].lgc.push_back({a[l],1});
		seg[now].rgc.push_back({a[l],1});
		if(a[l]!=1) seg[now].res=1;
		return ;
	}
	int mid=(l+r)>>1;
	if(wh<=mid) modify(son[now][0],l,mid,wh);
	else modify(son[now][1],mid+1,r,wh);
	seg[now]=pushup(seg[son[now][0]],seg[son[now][1]]);
} 

inline node query(int now,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr)
		return seg[now];
	int mid=(l+r)>>1;
	if(qr<=mid) return query(son[now][0],l,mid,ql,qr);
	else if(ql>mid) return query(son[now][1],mid+1,r,ql,qr);
	else return pushup(query(son[now][0],l,mid,ql,qr),query(son[now][1],mid+1,r,ql,qr));
} 

void solve(){
	n=rd(),m=rd();
	fp(i,1,n) a[i]=rd();
	build(root,1,n);
	while(m--){
		int op=rd(),x=rd(),y=rd();
		if(op==1) a[x]=y,modify(root,1,n,x);
		else{
			node p=query(1,1,n,x,y);
			cout << p.res << endl;
		}
	}
}

G

计数,计数,计数!
加练,加练,加练!

这个加边就很灵性

这个定向就很二分图,不是定这个向,就是这个向
所以可以想到一个二分图的方法

对于规定的这两个点之间的边,他们的方向都是一样的,这里把边映射到点上,中间的点用二并查集连起来

同时发现,如果这条路线转了个弯,则劈出的链是不一样的

这里一个点所代表的边与其父亲边连在一起,要求有一个点能继续向上连

const int maxN=3*1e5+10;
int n,k;
vector<int> g[maxN];
int acc[maxN][25],dep[maxN];
int f[maxN<<1],h[maxN];

int findx(int x){
	if(f[x]==x) return x;
	return f[x]=findx(f[x]);
}

void merge(int x,int y){f[findx(x)]=findx(y);}

inline void dfs1(int now,int f){
	acc[now][0]=f;
	dep[now]=dep[f]+1;
	for(int i=0,v=acc[now][i];v;v=acc[now][++i]) acc[now][i+1]=acc[v][i];
	for(int x:g[now]) if(x!=f) dfs1(x,now);
}

int lca(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	for(int gap=dep[x]-dep[y],bit=logx(gap);gap;gap-=(1<<bit),bit=logx(gap)) x=acc[x][bit];
	for(int i=logx(dep[x]);~i;--i) if(acc[x][i]!=acc[y][i]) x=acc[x][i],y=acc[y][i];
	return (x==y)?x:acc[x][0];
}

void dfs2(int now,int f){
	for(int x:g[now]){
		if(x==f) continue; 
		dfs2(x,now);
		h[now]=min(h[now],h[x]);
	}
	if(h[now]<dep[now]-1){
		merge(f,now);
		merge(f+n,now+n);
	}
}

int qmi(int p){
	int ans=1,x=2;
	for(;p;p>>=1){if(p&1) (ans*=x)%=mod;(x*=x)%=mod;}
	return ans;
}

void solve(){
	n=rd(),k=rd();
	fp(i,1,n-1){
		int u=rd(),v=rd();
		g[u].push_back(v),g[v].push_back(u); 
	}
	fp(i,1,n<<1) f[i]=i;
	dfs1(1,0);
	fp(i,1,n) h[i]=dep[i];
	while(k--){
		int a=rd(),b=rd();
		int c=lca(a,b);
		if(a==b) continue ;
		h[a]=min(h[a],dep[c]);
		h[b]=min(h[b],dep[c]);
		if(c!=a&&c!=b) merge(a+n,b),merge(a,b+n);
	}
	dfs2(1,0);
	int cnt=0;
	fp(i,1,n) 
		if(findx(i)==findx(i+n)) {
			cout << 0 << endl;
			return ;
		}
	fp(i,1,n<<1){
		if(i==findx(i))
			++cnt;
	}
	cnt-=2;
	cnt>>=1;
	cout << qmi(cnt) << endl;
}

F

喵喵题

非常的怪啊,这个题就是一个奇妙贪心

一开始大家的思路可能就是让伤害多的先上场然后再换就可以了,但是这个思路会在第三个样例狗带

然后你可能会想,我让总攻击力最强的先上不久行了,但是这样就有可能打不满了,值无法维护

可以从另一个角度来解读这个条件,对于这个对战,可以抽象成一个方格,向着个方格里面来填数,hp 可以抽象成有多少个物品,这样的话,就优先向里面放价值大的物品就可以了,为了少换人,就横着填

因为题目的特殊限定,所以这样是对的

const int maxN = 5 * 1e5 + 10;
int m, n;
struct node {
    int deg, hp, id;
} a[maxN];
int arr[7][maxN];

void solve() {
    m = rd(), n = rd();
    fp(i, 1, n) {
        int x = rd(), y = rd(), z = i;
        a[i] = { x, y, z };
    }
    sort(a + 1, a + n + 1, [&](node x, node y) { return x.deg > y.deg; });
    int p = 1, thp = a[p].hp;
    ll maxx = 0;
    fp(k, 1, 6)
        fp(j, 1, m) {
            arr[k][j] = a[p].id, maxx += a[p].deg;
            --thp;
            if (!thp)
                ++p, thp = a[p].hp;
        }
    cout << maxx << endl;
    ll op = 0;
    set<int> st;
    fp(i, 1, 6) st.insert(arr[i][1]);
    fp(i, 2, m) {
        vector<int> v;
        set<int> st2;
        fp(k, 1, 6) if (st.count(arr[k][i])) st.erase(arr[k][i]), st2.insert(arr[k][i]);
        else v.push_back(arr[k][i]), st2.insert(arr[k][i]);
        op += st.size();
        swap(st2, st);
    }
    fp(i, 1, 6) cout << arr[i][1] << ' '; cout << endl;
    cout << op << endl;
    st.clear();
    fp(i, 1, 6) st.insert(arr[i][1]);
    fp(i, 2, m) {
        vector<int> v;
        set<int> st2;
        fp(k, 1, 6) if (st.count(arr[k][i])) st.erase(arr[k][i]), st2.insert(arr[k][i]);
        else v.push_back(arr[k][i]), st2.insert(arr[k][i]);
        for (int x : st) cout << i - 1 << ' ' << x << ' ' << v.back() << endl, v.pop_back();
        swap(st2, st);
    }
}

E

明显有搜索的解法

然后折个半

最后统计答案的时候就lower_bound 一下就可以了

const int maxN = 100;
int n, lim1, lim2, k;
ll ans = 0;
pii a[maxN];
vector<pii> v1;
vector<int> v2[maxN];

void lis() {
    vector<int> v;
    fp(i, 1, n) v.push_back(a[i].fir);
    sort(v.begin(), v.end());
    int len = unique(v.begin(), v.end()) - v.begin();
    while (v.size() != len) v.pop_back();
    fp(i, 1, n) a[i].fir = lower_bound(v.begin(), v.end(), a[i].fir) - v.begin() + 1;
    v.clear();
}

inline void dfs1(int now, ll sum) {
    if (now)
        v1.push_back({ sum, a[now].fir });
    if (sum >= k)
        ++ans;
    fp(i, now + 1, lim1) if (a[now].fir <= a[i].fir) dfs1(i, sum + a[i].sec);
}

inline void dfs2(int now, ll sum) {
    if (now != n + 1)
        v2[now].push_back(sum);
    if (sum >= k)
        ++ans;
    fd(i, now - 1, lim2) if (a[now].fir >= a[i].fir) dfs2(i, sum + a[i].sec);
}

void solve() {
    n = rd(), k = rd();
    fp(i, 1, n) a[i].fir = rd(), a[i].sec = rd();
    a[0].fir = 0;
    lis();
    lim1 = n >> 1;
    lim2 = lim1 + 1;
    a[n + 1].fir = inf;
    dfs1(0, 0);
    dfs2(n + 1, 0);
    fp(i, 1, n) sort(v2[i].begin(), v2[i].end());
    fp(i, 1, v1.size()) {
        auto temp = v1[i - 1];
        fp(j, lim2, n) if (temp.sec <= a[j].fir) {
            int t = lower_bound(v2[j].begin(), v2[j].end(), k - temp.fir) - v2[j].begin();
            if (v2[j].empty())
                continue;
            if (v2[j].back() < k - temp.fir)
                continue;
            ans += v2[j].size() - t;
        }
    }
    cout << ans << endl;
}

D

傻逼题,翻转的贡献最多只有 O(n) 个位置,处理出来就行了

const int maxN = 5 * 1e5 + 10;
int n;
int a[maxN << 1], pos[maxN << 1], pre[maxN << 1];
vector<int> v[maxN << 1];

int dis(int x, int y) { return max(x, y) - min(x, y); }

int q(int l, int r) { return pre[r] - pre[l - 1]; }

void solve() {
    n = rd();
    fp(i, 1, n) a[(i << 1) - 1] = rd(), a[i << 1] = 0;
    n *= 2;
    fp(i, 1, n) if (a[i]) pos[a[i]] = i;
    fp(i, 1, n >> 1) {
        if (i * 2 - 1 != pos[i])
            v[(i * 2 - 1 + pos[i]) >> 1].push_back(dis(i * 2 - 1, pos[i]) >> 1);
    }
    fp(i, 1, n) sort(v[i].begin(), v[i].end());
    fp(i, 1, n) pre[i] = pre[i - 1] + ((a[i] * 2 - 1) == i);
    int ans = 0;
    fp(i, 1, n) {
        int sum = 0;
        for (int x : v[i]) {
            ++sum;
            int tot = sum - q(i - x, i + x) + ((a[i] * 2 - 1) == i);
            ans = max(tot, ans);
        }
    }
    cout << ans << endl;
}

A-C

水省

posted @   颈流推进  阅读(14)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示