2023年国际大学生程序设计竞赛(ACM-ICPC)新疆赛区 A.The Number Of Black Edges

Posted Code Fan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2023年国际大学生程序设计竞赛(ACM-ICPC)新疆赛区 A.The Number Of Black Edges相关的知识,希望对你有一定的参考价值。

传送门

大致题意:

  爱丽丝得到一棵树,树上有n个节点,索引从1到n。树上的每条边可以是黑色或白色,所有的边最初都是白色的。有三种操作: 1. 将一条边的颜色改为黑色。2. 将一条边的颜色改为白色。3. 3.给定一个节点索引,计算从所有奇数节点到该节点的简单路径上的黑色边的数量之和。请注意,简单路径是指两个节点之间的路径,该路径对任何节点的访问都不超过一次。读入一个n和一个m,紧接着读入n-1行,每行两个节点u,v表示u和v有连边,第几行就表示是第几个边。读入m个询问,询问如提议所示。

解题思路:

  发现如果有一条边从白色变成了黑色,我们可以认为黑的边把这个树分成了左右两个部分,右边的任意一个点到右边的任意一个点的简单路径都不会经过这个黑色的边,也就是说左边的点无法对左边的点造成任何贡献,右边的点无法对右边的点造成任何贡献。但是左边的点和右边的点可能会有贡献。如果我们知道左边的奇数的点的数量是x1,右边的奇数点的数量为x2,那么右边所有点被奇数点的拜访且经过黑色边的次数就会增加x1的数量,左边所有点被奇数点拜访的数量且经过黑色边的次数都会加x2。所以考虑用线段树维护每个点答案即可。树上跑dfs序建线段树,统计出每个点的子树大小和深度和子树有多少奇数结点。对于第x条边我们可以知道第x个边对应的两个点是谁,我们已经预处理求出深度最大的那个点中有多少奇数点数量sum,那么深度小的那个点对应的奇数结点数量就是(n + 1) / 2 - sum,sum就是我们说的x1, (n + 1) / 2 - sum 就是说的x2。需要注意的地方就是需要开一个数组记录当前边的颜色,如果把一个白色边染成白色或者把一个黑色边染成黑色是不会对答案造成任何影响的。

#include <bits/stdc++.h>

const int N = 1e5 + 10;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
using ll = long long;

typedef std::pair<int, int> PII;

int a[N];
int n, m;

int h[N], ne[N << 1], e[N << 1], idx;
int sz[N], fa[N], id[N], rid[N], timedelta, dep[N], odd[N];
bool color[N];

inline void add(int a, int b) 
	ne[idx] = h[a], e[idx] = b, h[a] = idx ++;


inline void dfs(int u, int father, int depth) 
	id[u] = ++ timedelta, rid[timedelta] = u;
	sz[u] = 1; dep[u] = depth; 
	if (u & 1) odd[u] = 1;
	for (int i = h[u]; ~i; i = ne[i]) 
		int j = e[i];
		if (j == father) continue;
		dfs(j, u, depth + 1);
		sz[u] += sz[j];
		odd[u] += odd[j];
	


struct node 
	ll ans, tag;
tr[N << 2];

#define ls u << 1
#define rs u << 1 | 1

inline void pushdown(int u) 
	if (tr[u].tag) 
		tr[ls].tag += tr[u].tag;
		tr[rs].tag += tr[u].tag;
		tr[ls].ans += tr[u].tag;
		tr[rs].ans += tr[u].tag;
		tr[u].tag = 0;
	


inline void modify(int u, int L, int R, int l, int r, int v, int how) 
	if (L >= l && R <= r) 
		if (how) 
			if (L == R) tr[u].ans += v;
			else tr[u].tag += v;
		 else 
			if (L == R) tr[u].ans -= v;
			else tr[u].tag -= v;
		
		return ;
	
	
	pushdown(u);
	
	int mid = L + R >> 1;
	if (l <= mid) modify(ls, L, mid, l, r, v, how);
	if (r > mid) modify(rs, mid + 1, R, l, r, v, how);


inline ll query(int u, int L, int R, int x) 
	if (L == R) return tr[u].ans;
	
	pushdown(u);
	
	int mid = L + R >> 1;
	if (x <= mid) return query(ls, L, mid, x);
	return query(rs, mid + 1, R, x);


inline void solve() 
	memset(h, -1, sizeof h);
	
	std::cin >> n >> m;
	
	std::vector<PII> edge; 
	for (int i = 2; i <= n; i ++) 
		int a, b;
		std::cin >> a >> b;
		add(a, b);
		add(b, a);
		edge.push_back(a, b);
	
	
	int tot = n + 1 >> 1; // 编号为奇数的点的个数
	
	dfs(1, -1, 1);
	
	while (m --) 
		int op, x;
		std::cin >> op >> x;
		if (op == 2)  // 染成黑色
		    x --;
			if (!color[x]) continue;
			color[x] = false;
			auto [u, v] = edge[x];
		
			if (dep[u] < dep[v]) std::swap(u, v);
			int top = tot - odd[u];
			modify(1, 1, n, id[u], id[u] + sz[u] - 1, top, 0);
			if (id[u] > 1)
			modify(1, 1, n, 1, id[u] - 1, odd[u], 0);
			if (id[u] + sz[u] <= n)
			modify(1, 1, n, id[u] + sz[u], n, odd[u], 0); 
		 else if (op == 1)  // 染成白色
		    x --;
			if (color[x]) continue;
			color[x] = true;
			auto [u, v] = edge[x];
			if (dep[u] < dep[v]) std::swap(u, v);
			int top = tot - odd[u];
			modify(1, 1, n, id[u], id[u] + sz[u] - 1, top, 1);
			if (id[u] > 1) 
			modify(1, 1, n, 1, id[u] - 1, odd[u], 1);
			if (id[u] + sz[u] - 1 < n)
			modify(1, 1, n, id[u] + sz[u], n, odd[u], 1); 
		 else  // 求答案
			std::cout << query(1, 1, n, id[x]) << \'\\n\';
		
		
	



int main() 
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    int _ = 1;
    //std::cin >> _;
    while (_ --) solve();
    
    return 0;

ACM-ICPC国际大学生程序设计竞赛北京赛区(2017)网络赛

题目1 : Visiting Peking University

时间限制:1000ms
单点时限:1000ms
内存限制:256MB

描述

Ming is going to travel for n days and the date of these days can be represented by n integers: 0, 1, 2, …, n-1. He plans to spend m consecutive days(2 ≤ m ≤ n)in Beijing. During these m days, he intends to use the first day and another day to visit Peking university. Before he made his plan, Ming investigated on the number of tourists who would be waiting in line to enter Peking university during his n-day trip, and the results could be represented by an integer sequence p[i] (0 ≤ i ≤ n-1, p[i] represents the number of waiting tourists on day i). To save time, he hopes to choose two certain dates a and b to visit PKU(0 ≤ a < b ≤ n-1), which makes p[a] + p[b] as small as possible.

Unfortunately, Ming comes to know that traffic control will be taking place in Beijing on some days during his n-day trip, and he won’t be able to visit any place in Beijing, including PKU, on a traffic control day. Ming loves Beijing and he wants to make sure that m days can be used to visit interesting places in Beijing. So Ming made a decision:  spending k (m ≤ k ≤ n) consecutive days in Beijing is also acceptable if there are k - m traffic control days among those k days. Under this complicated situation, he doesn’t know how to make the best schedule. Please write a program to help Ming determine the best dates of the two days to visit Peking University.  Data guarantees a unique solution.

输入

There are no more than 20 test cases.

For each test case:

The first line contains two integers, above mentioned n and m (2 ≤ n ≤ 100, 2 ≤ m ≤ n).

The second line contains n integers, above mentioned p[0] , p[1] , … p[n-1]. (0 ≤ p[i] ≤ 1000, i = 0 ... n-1)

The third line is an integer q (0 ≤ q ≤ n), representing the total number of traffic control days during the n-day trip, followed by q integers representing the dates of these days.

输出

One line, including two integers a and b, representing the best dates for visiting PKU.

样例输入
7 3
6 9 10 1 0 8 35
3 5 6 2
4 2
10 11 1 2
1 2
样例输出
0 3
1 3

这个题意感觉有些模糊啊,就是给你n天,每天都有个权值,但是有几天是不能玩的,让你算算选两天,必须选一天和m天中的另一天,和最小

记录下合法的,直接暴力吧

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int main()
{
    int a[101],i,j,n,m,x,y,s,h,u,v,k;
    while(~scanf("%d%d",&n,&m))
    {
        for(i=0;i<n;i++)
        scanf("%d",&a[i]);
        scanf("%d",&x);
        for(i=0;i<x;i++)
        {
            scanf("%d",&y);
            a[y]=-1;
        }
        s=10000;u=v=0;x=y=0;
        for(i=0;i<n;i++)
        {
            if(a[i]==-1)
            continue;
            k=m;
            h=a[i];
            for(j=i+1;j<k+i&&j<n;j++)
            {
                if(a[j]==-1)
                {
                    k++;
                    continue;
                }
                if(h+a[j]<s)
                {
                    s=h+a[j];
                    u=i,v=j;
                }
            }
            if(k+i>n)
            break;
            else x=u,y=v;
        }
        printf("%d %d\\n",x,y);
    }
} 

题目9 : Minimum

时间限制:1000ms
单点时限:1000ms
内存限制:256MB

描述

You are given a list of integers a0, a1, …, a2^k-1.

You need to support two types of queries:

1. Output Minx,y∈[l,r] {ax∙ay}.

2. Let ax=y.

输入

The first line is an integer T, indicating the number of test cases. (1≤T≤10).

For each test case:

The first line contains an integer k (0 ≤ k ≤ 17).

The following line contains 2k integers, a0, a1, …, a2^k-1 (-2k ≤ ai < 2k).

The next line contains a integer  (1 ≤ Q < 2k), indicating the number of queries. Then next Q lines, each line is one of:

1. 1 l r: Output Minx,y∈[l,r]{ax∙ay}. (0 ≤ l ≤ r < 2k)

2. 2 x y: Let ax=y. (0 ≤ x < 2k, -2k ≤ y < 2k)

输出

For each query 1, output a line contains an integer, indicating the answer.

样例输入
1
3
1 1 2 2 1 1 2 2
5
1 0 7
1 1 2
2 1 2
2 2 2
1 1 2
样例输出
1
1
4

裸的线段树,需要维护区间最大值最小值,但是最小值最大值的正负未知,需要分情况讨论

#include <bits/stdc++.h>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=2e5+5;
int amax[maxn<<2];
int amin[maxn<<2];
int b[maxn];
const int inf=1<<30;
void pushup_max(int rt)
{
    amax[rt]=max(amax[rt<<1],amax[rt<<1|1]);
}
void pushup_min(int rt)
{
    amin[rt]=min(amin[rt<<1],amin[rt<<1|1]);
}
void build_max(int l,int r,int rt)
{
    if(l==r)
    {
        amax[rt]=b[l];
        return ;
    }
    int m=(l+r)>>1;
    build_max(lson);
    build_max(rson);
    pushup_max(rt);
}
void build_min(int l,int r,int rt)
{
    if(l==r)
    {
        amin[rt]=b[l];
        return ;
    }
    int m=(l+r)>>1;
    build_min(lson);
    build_min(rson);
    pushup_min(rt);
}
void update_max(int pos,int bue,int l,int r,int rt)
{
    if(l==r)
    {
        amax[rt]=bue;
        return;
    }
    int m=(l+r)>>1;
    if(pos<=m) update_max(pos,bue,lson);
    else update_max(pos,bue,rson);
    pushup_max(rt);
}
void update_min(int pos,int bue,int l,int r,int rt)
{
    if(l==r)
    {
        amin[rt]=bue;
        return ;
    }
    int m=(l+r)>>1;
    if(pos<=m) update_min(pos,bue,lson);
    else update_min(pos,bue,rson);
    pushup_min(rt);
}
int query_max(int left,int right,int l,int r,int rt)
{
    if(left<=l&&right>=r)
    {
        return amax[rt];
    }
    int m=(l+r)>>1;
    int t=-inf;
    if(left<=m) t=max(t,query_max(left,right,lson));
    if(right>m) t=max(t,query_max(left,right,rson));
    return t;
}
int query_min(int left,int right,int l,int r,int rt)
{
    if(left<=l&&right>=r)
    {
        return amin[rt];
    }
    int m=(l+r)>>1;
    int t=inf;
    if(left<=m) t=min(t,query_min(left,right,lson));
    if(right>m) t=min(t,query_min(left,right,rson));
    return t;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        n=1<<n;
        for(int i=1; i<=n; i++)
            scanf("%d",b+i);
        build_max(1,n,1);
        build_min(1,n,1);
        int m;
        scanf("%d",&m);
        for(int i=0; i<m; i++)
        {
            int x,l,r;
            scanf("%d%d%d",&x,&l,&r);
            if(x==1)
            {
                int mi=query_min(++l,++r,1,n,1);
                int ma=query_max(l,r,1,n,1);
                if(ma*1LL*mi<=0)printf("%lld\\n",ma*1LL*1LL*mi);
                else
                printf("%lld\\n",min(ma*1LL*ma,mi*1LL*mi));
            }
            else
            {
                update_max(++l,r,1,n,1);
                update_min(l,r,1,n,1);
            }
        }
    }
    return 0;
}

 

题目7 : Bounce

时间限制:1000ms
单点时限:1000ms
内存限制:256MB

描述

For Argo, it is very interesting watching a circle bouncing in a rectangle.

As shown in the figure below, the rectangle is divided into N×M grids, and the circle fits exactly one grid.

The bouncing rule is simple:

1. The circle always starts from the left upper corner and moves towards lower right.

2. If the circle touches any edge of the rectangle, it will bounce.

3. If the circle reaches any corner of the rectangle after starting, it will stop there.

Argo wants to know how many grids the circle will go through only once until it first reaches another corner. Can you help him?

输入

The input consists of multiple test cases. (Up to 105)

For each test case:

One line contains two integers N and M, indicating the number of rows and columns of the rectangle. (2 ≤ N, M ≤ 109)

输出

For each test case, output one line containing one integer, the number of grids that the circle will go through exactly once until it stops (the starting grid and the ending grid also count).

样例输入
2 2
2 3
3 4
3 5
4 5
4 6
4 7
5 6
5 7
9 15
样例输出
2
3
5
5
7
8
7
9
11
39

找规律,死在这上面了,很难找啊。绝望

题解源于qls,我这种死活动手推规律的傻逼了

献上pp的代码,啧啧称奇

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

int main(int argc, char *argv[]) {
  cin.sync_with_stdio(false);
  ll n, m;
  while (cin >> n >> m) {
    if (n < m) swap(n, m);
    ll gcd = __gcd(n - 1, m - 1);
    ll k = (m - 1) / gcd;
    ll x = (n - 1) / gcd;
    ll ans = (k - 1) * (m - k + 1) + (x - k + 1) * (m - k);
    cout << ans + 1 << endl;
  }
  return 0;
}

 

以上是关于2023年国际大学生程序设计竞赛(ACM-ICPC)新疆赛区 A.The Number Of Black Edges的主要内容,如果未能解决你的问题,请参考以下文章

ACM-ICPC国际大学生程序设计竞赛北京赛区(2017)网络赛

ACM-ICPC国际大学生程序设计竞赛北京赛区(2017)网络赛

acm金牌啥水平

ACM-ICPC国际大学生程序设计竞赛北京赛区(2016)网络赛 A Simple Job

ACM-ICPC国际大学生程序设计竞赛北京赛区(2016)网络赛 The Book List

ACM-ICPC国际大学生程序设计竞赛北京赛区(2017)网络赛 题目9 : Minimum