栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

2018-2019 ACM-ICPC Brazil Subregional Programming Contest题解

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

2018-2019 ACM-ICPC Brazil Subregional Programming Contest题解

全补完了,发个题解。

A.Slackline Adventure

题意:给定n * m个点,求出有多少点对满足两点连线上没有其他点,且两点之间距离在l到r之间。

做法:两点相邻的情况单独计算。那么每一个点对等价于一个矩形,矩形对角线上没有点且对角线长度在l到r之间。

考虑枚举矩形的长和宽,可以得到答案为 2 ∑ i = 1 n − 1 ∑ j = 1 m − 1 ( n − i ) ( m − j ) [ g c d ( i , j ) = 1 ] [ l 2 ≤ i 2 + j 2 ≤ r 2 ] 2sum_{i = 1}^{n - 1} sum_{j = 1}^{m - 1} (n - i)(m - j) [gcd(i,j) = 1][l^2 leq i^2 + j^2 leq r^2] 2∑i=1n−1​∑j=1m−1​(n−i)(m−j)[gcd(i,j)=1][l2≤i2+j2≤r2]

显然这个东西可以用莫比乌斯反演来处理,枚举gcd得到

2 ∑ d = 1 n − 1 μ ( d ) ∑ i = 1 n − 1 d ( n − i ) ∑ j = 1 m − 1 d ( m − j ) [ l 2 ≤ i 2 + j 2 ≤ r 2 ] 2sum_{d = 1}^{n - 1}mu(d)sum_{i = 1}^{frac{n - 1}{d}}(n - i)sum_{j = 1}^{frac{m - 1}{d}}(m - j)[l^2 leq i^2 + j^2 leq r^2] 2∑d=1n−1​μ(d)∑i=1dn−1​​(n−i)∑j=1dm−1​​(m−j)[l2≤i2+j2≤r2]

那么分别计算 i 2 + j 2 ≤ r 2 i^2 + j^2 leq r^2 i2+j2≤r2的答案和 i 2 + j 2 ≤ l 2 − 1 i^2 + j^2 leq l^2 - 1 i2+j2≤l2−1的答案,相减即可。

即计算下面这个式子
2 ∑ d = 1 n − 1 μ ( d ) ∑ i = 1 n − 1 d ( n − i ) ∑ j = 1 m − 1 d ( m − j ) [ i 2 + j 2 ≤ k ] 2sum_{d = 1}^{n - 1}mu(d)sum_{i = 1}^{frac{n - 1}{d}}(n - i)sum_{j = 1}^{frac{m - 1}{d}}(m - j)[i^2 + j^2 leq k] 2∑d=1n−1​μ(d)∑i=1dn−1​​(n−i)∑j=1dm−1​​(m−j)[i2+j2≤k]

枚举前两个求和符号是 O ( n l o g n ) O(nlogn) O(nlogn)的复杂度,然后二分算出第三部分的答案,那么总的复杂度就是
O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

#include 
#include 
#include 
#include 
#define int long long
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const int p = 1e9 + 7;
int pp[maxn];
int n, m, l, r;
int prime[maxn], mu[maxn];

void sieve()
{
    mu[1] = 1;
    for ( int i = 2;i <= 1e5;i++ ) {
        if ( !pp[i] ) {
            prime[++prime[0]] = i;
            mu[i] = -1;
        }
        for ( int j = 1;j <= prime[0];j++ ) {
            if ( prime[j] * i > 1e5 ) {
                break;
            }
            pp[i * prime[j]] = 1;
            if ( i % prime[j] == 0 ) {
                mu[i * prime[j]] = 0;
                break;
            }
            mu[i * prime[j]] = -mu[i];
        }
    }
}

ll calc(ll d, ll i, ll t)
{
    int l = 0, r = (m - 1) / d + 1;
    int tt = 0;
    while ( l < r ) {
        int mid = (l + r) >> 1;
        if ( 1ll * d * d * i * i + 1ll * d * d * mid * mid <= t ) {
            tt = mid;
            l = mid + 1;
        }
        else {
            r = mid;
        }
    }
    return (1ll * tt * m % p - 1ll * d * ((1ll * tt * (tt + 1) / 2) % p) % p) % p;
}

ll f(ll n, ll m, ll t)
{
    ll ret = 0;
    for ( ll i = 1;i <= n - 1;i++ ) {
        ll temp = 0;
        for ( ll j = 1;j <= (n - 1) / i;j++ ) {
            temp = (temp + (n - j * i % p) * calc(i, j, t) % p) % p;
        }
        ret = (ret + temp * mu[i]) % p;
    }
    return ret;
}

signed main()
{
    sieve();
    scanf("%lld%lld%lld%lld", &n, &m, &l, &r);
    ll maxx = f(n, m, 1ll * r * r);
    ll minn = f(n, m, 1ll * l * l - 1);
    ll ans = (maxx - minn) * 2 % p;
    if ( l == 1 ) {
        ans = (1ll * (n - 1) * m % p + 1ll * (m - 1) * n % p + ans) % p;
    }
    printf("%lldn", ((ans) % p + p) % p);
    return 0;
}
B.Marbles

每一枚棋子最终都会聚集在 ( 1 , 2 ) (1,2) (1,2)和 ( 2 , 1 ) (2,1) (2,1),那么求一下sg函数即可

#include 

using namespace std;

int sg[105][105], f[400], x[1010], y[1010];

int main()
{
    sg[1][2] = sg[2][1] = 0;
    int i, j, k, n;
    for ( i = 1; i <= 100; i++ ) {
        for ( j = 1; j <= 100; j++ ) {
            if ( i == j ) continue;
            memset(f, 0, sizeof f);
            for ( k = 1; k <= max(i, j); k++ ) {
                int l = i - k, r = j - k;
                if ( l > 0 && l != j ) f[sg[l][j]] = 1;
                if ( r > 0 && i != r ) f[sg[i][r]] = 1;
                if ( l > 0 && r > 0 ) f[sg[l][r]] = 1;
            }
            for ( k = 0; k <= 400; k++ ) {
                if ( !f[k] ) {
                    sg[i][j] = k;
                    break;
                }
            }
        }
    }
    cin >> n;
    int ff = 0, ans = 0;
    for ( i = 1; i <= n; i++ ) cin >> x[i] >> y[i];
    for ( i = 1; i <= n; i++ ) {   
        if ( x[i] == 0 || y[i] == 0 || x[i] == y[i] ) {
            ff = 1;
            break;
        }
        ans ^= sg[x[i]][y[i]];
    }
    if ( ff || ans ) cout << "Y" << endl;
    else cout << "N" << endl;
    return 0;
}
C.Pizza Cutter

切出来的最大块数就等于纵向的线的交点数+横向的线的交点数+ ( H + 1 ) ( V + 1 ) (H + 1)(V + 1) (H+1)(V+1)
交点数只需要按照一边的坐标排个序,然后求逆序对数即可。

#include

using namespace std;

const long long N = 1e5+100;

long long ans, tr[N << 2], mx, maxx, maxy;
long long v, h, use[N << 2];

long long lowbit(long long x){
	return x & (-x);
}

struct ss1{
	long long you, zuo;

	bool operator < (const ss1 &a )const{
		return zuo < a.zuo;
	}
}hh[N << 1];

struct ss2{
	long long shang, xia;
	bool operator < (const ss2 &a )const{
		return shang < a.shang;
	}
}vv[N << 1];

bool cmp_zuo(ss2 a, ss2 b){
	return a.shang < b.shang;
}

long long add(long long x){
	while(x <= mx)
		tr[x] ++, x += lowbit(x);
	return x;
}

long long asksum(long long x){
	long long re = 0;
	while(x)
		 re += tr[x], x -= lowbit(x);
	return re;
}

long long ask(long long l, long long r){
	return asksum(r) - asksum(l - 1);
}

long long read(){
	
	long long x;
	scanf("%lld", &x);
	return x;
}

int main(){
	
	cin >> maxx >> maxy;
	cin >> h >> v;
	mx = max(h * 2, v * 2);
	
	for(long long i = 1; i <= h; i ++)
		use[i] = hh[i].zuo = read(), use[i + h] = hh[i].you = read();
	sort(use + 1, use + h * 2 + 1);
	for(long long i = 1; i <= h; i ++)
		hh[i].zuo = lower_bound(use + 1, use + h * 2 + 1, hh[i].zuo) - use,
		hh[i].you = lower_bound(use + 1, use + h * 2 + 1, hh[i].you) - use;
		
	for(long long i = 1; i <= v; i ++)
		use[i] = vv[i].shang = read(), use[i + v] = vv[i].xia = read();
	sort(use + 1, use + v * 2 + 1);
	for(long long i = 1; i <= v; i ++)
		vv[i].shang = lower_bound(use + 1, use + v * 2 + 1, vv[i].shang) - use,
		vv[i].xia = lower_bound(use + 1, use + v * 2 + 1, vv[i].xia) - use;	
		
	sort(hh + 1, hh + h + 1);
	sort(vv + 1, vv + v + 1);
	
	for(long long i = 1; i <= h; i ++){
		long long zuo = hh[i].zuo;
		long long you = hh[i].you;
		ans += ask(you + 1, mx);
		add(you);
	}
	
	memset(tr, 0, sizeof(tr));
	
	for(long long i = 1; i <= v; i ++){
		long long xia = vv[i].xia;
		ans += ask(xia + 1, mx);
		add(xia);
	}
	
	cout << ans + (h + 1) * (v + 1);
	
}
D.Unraveling Monty Hall

题面比较长,只需要求n - 1的数量即可。

#include 
#include 
using namespace std;

int main()
{
    int n;
    scanf("%d", &n);
    int ans = 0;
    for ( int i = 1;i <= n;i++ ) {
        int x;
        scanf("%d", &x);
        if ( x != 1 )
            ans++;
    }
    printf("%dn", ans);
    return 0;
}
E.Enigma

n 2 n^2 n2暴力就能过,我们队写了个bitset优化,写复杂了

#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 1e4 + 10;
bitset bt1[30], bt2[30];
char s[maxn], t[maxn];

int main()
{
    scanf("%s", s + 1);
    scanf("%s", t + 1);
    int n = strlen(s + 1);
    int m = strlen(t + 1);
    for ( int i = 1;i <= m;i++ ) {
        bt1[t[i] - 'A'][i] = 1;
        bt2[s[i] - 'A'][i] = 1;
    }
    int ans = 0;
    for ( int i = 1;i + m - 1 <= n;i++ ) {
        int flag = 0;
        for ( int j = 0;j <= 25;j++ ) {
            bitset temp = bt1[j] & bt2[j];
            if ( temp.count() != 0 ) {
                flag = 1;
            }
        }
        if ( flag == 0 )
            ans++;
        bt2[s[i] - 'A'][1] = 0;
        for ( int j = 0;j <= 25;j++ ) {
            bt2[j] >>= 1;
            bt2[j][0] = 0;
        }
        if ( i + m <= n )
            bt2[s[i + m] - 'A'][m] = 1;
    }
    printf("%dn", ans);
    return 0;
}
F.Music Festival

入门状压dp,离散化一下就行了。

#include

using namespace std;

const int N = 2050;

int dp[N][N], use[N << 2], nn, m, mi;
vector < int > yanchu[N];

struct ss{
	int s, e, song, st;
}prog[N];

bool cmp_by_e(ss a, ss b){
	return a.e < b.e;
}

int read(){
	
	int x;
	scanf("%d", &x);
	return x;
}

int main(){
	
	cin >> nn;
	for(int i = 0; i <= nn - 1; i ++){
		mi = read();
		for(int j = 1; j <= mi; j ++)
			prog[++ m].s = read(), prog[m].e = read(), prog[m].song = read(), prog[m].st = i;
	}
	
	sort(prog + 1, prog + m, cmp_by_e);

	for(int i = 1; i <= m; i ++)
		use[i] = prog[i].s, use[i + m] = prog[i].e;
	sort(use + 1, use + m * 2 + 1);
	for(int i = 1; i <= m; i ++)
		prog[i].s = lower_bound(use + 1, use + m * 2 + 1, prog[i].s) - use,
		prog[i].e = lower_bound(use + 1, use + m * 2 + 1, prog[i].e) - use;
	
	for(int i = 1; i <= m; i ++)
		yanchu[prog[i].e].push_back(i);
	
	for(int z = 0; z <= (1 << nn) - 1; z ++)
		for(int i = 1; i <= m * 2; i ++){
			dp[i][z] = dp[i - 1][z];
			for(int j = 0; j <= (int)yanchu[i].size() - 1; j ++){
				int zz = (1 << prog[yanchu[i][j]].st), s = prog[yanchu[i][j]].s, v = prog[yanchu[i][j]].song;
				if((zz & z) && (dp[s][z] || dp[s][z ^ zz] || z == 0 || (z ^ zz) == 0))
					dp[i][z] = max(dp[i][z], max(dp[s][z], dp[s][z ^ zz]) + v);
			}
		}
		
	if(dp[m * 2][(1 << nn) - 1] == 0)
		cout << -1;
	else cout << dp[m * 2][(1 << nn) - 1];
} 
G.Gasoline

边权从小到大加边然后跑网络流判断是否满流即可,或者二分最大边权后跑网络流。

#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 4e3 + 10;
struct Edge {
    int val, to, next;
}e[1000000];
struct EDGE {
    int u, v, w;
    bool operator<(const EDGE& x)const
    {
        return w < x.w;
    }
}a[100000];
int head[maxn], tot = 1;
int dep[maxn], cur[maxn];
int n, m, c;
int s, t;

void addedge(int u, int v, int w)
{
    e[++tot] = Edge({ w,v,head[u] });
    head[u] = tot;
    e[++tot] = Edge({ 0,u,head[v] });
    head[v] = tot;
}

bool bfs()
{
    for ( int i = 0;i <= n + m + 1;i++ ) {
        dep[i] = 0;
        cur[i] = head[i];
    }
    queue qq;
    qq.push(s);
    dep[s] = 1;
    while ( !qq.empty() ) {
        int temp = qq.front();
        qq.pop();
        for ( int i = head[temp];~i;i = e[i].next ) {
            int to = e[i].to;
            if ( e[i].val && !dep[to] ) {
                dep[to] = dep[temp] + 1;
                if ( to == t )
                    return 1;
                qq.push(to);
            }
        }
    }
    return 0;
}

int dfs(int now = s, int flow = 0x3f3f3f3f)
{
    if ( now == t )
        return flow;
    int ret = 0;
    for ( int& i = cur[now];~i;i = e[i].next ) {
        int to = e[i].to;
        if ( e[i].val && dep[to] == dep[now] + 1 ) {
            int temp = dfs(to, min(e[i].val, flow));
            ret += temp;
            e[i].val -= temp;
            e[i ^ 1].val += temp;
            if ( (flow -= temp) <= 0 )
                break;
        }
    }
    if ( ret == 0 )
        dep[now] = 0;
    return ret;
}

int main()
{
    memset(head, -1, sizeof head);
    scanf("%d%d%d", &n, &m, &c);
    s = 0, t = n + m + 1;
    int sum = 0;
    for ( int i = 1;i <= n;i++ ) {
        int x;
        scanf("%d", &x);
        addedge(m + i, t, x);
        sum += x;
    }
    for ( int i = 1;i <= m;i++ ) {
        int x;
        scanf("%d", &x);
        addedge(s, i, x);
    }
    int ans = -1;
    for ( int i = 1;i <= c;i++ ) {
        scanf("%d%d%d", &a[i].u, &a[i].v, &a[i].w);
    }
    sort(a + 1,a + c + 1);
    int temp = 0;
    for ( int i = 1;i <= c;i++ ) {
        addedge(a[i].v, a[i].u + m, 0x3f3f3f3f);
        while ( bfs() ) {
            temp += dfs();
        }
        if ( temp == sum ) {
            ans = a[i].w;
            break;
        }
    }
    printf("%dn", ans);
    return 0;
}
H.Police Hypothesis

给定一棵节点上带字符的树,给定一个模式串,需要支持两种操作:

  • 查询从 u u u到 v v v的字符串中包含多少模式串
  • 修改某个节点上的字符

由于模式串长度最多只有100,所以可以树剖后用线段树维护长度为0~100的前缀哈希值和后缀哈希值,合并时的答案为左儿子的答案+右儿子的答案+跨过中间的答案。跨过中间的答案只需要暴力左儿子中的后缀长度来计算即可。

由于 u u u到 v v v和 v v v到 u u u的路径不同,所以我们还需要维护反过来看的字符串。

总复杂度 O ( 100 n l o g n ) O(100nlogn) O(100nlogn)

#include 
#include 
#include 
#include 
#include 
#define lson ( p << 1 )
#define rson ( p << 1 | 1 )
using namespace std;
typedef long long ll;
typedef unsigned int ull;
const int maxn = 1e5 + 1;
int n, q;
char s[101];
char a[maxn];
struct Edge {
    int to, next;
}e[maxn << 1];
int head[maxn], tot;
int dfn[maxn], dep[maxn], top[maxn], fa[maxn], son[maxn];
int dfnn;
int id[maxn];
int len;
ull has;
struct Tree {
    int len;
    int val;
    ull l[101], r[101];
}tree[maxn << 2][2];
ull po[maxn];

void addedge(int u, int v)
{
    e[++tot] = Edge({ v,head[u] });
    head[u] = tot;
}

void dfs1(int now = 1, int last = 0, int deep = 1)
{
    top[now] = 1;
    fa[now] = last;
    dep[now] = deep;
    int maxson = -1;
    for ( int i = head[now];~i;i = e[i].next ) {
        int to = e[i].to;
        if ( to == last )
            continue;
        dfs1(to, now, deep + 1);
        top[now] += top[to];
        if ( top[to] > maxson ) {
            maxson = top[to];
            son[now] = to;
        }
    }
}

void dfs2(int now = 1, int topf = 1)
{
    top[now] = topf;
    dfn[now] = ++dfnn;
    id[dfnn] = now;
    if ( !son[now] )
        return;
    dfs2(son[now], topf);
    for ( int i = head[now];~i;i = e[i].next ) {
        int to = e[i].to;
        if ( to == fa[now] || to == son[now] ) {
            continue;
        }
        dfs2(to, to);
    }
}

Tree combine(Tree x, Tree y)
{
    Tree ret {};
    ret.val = x.val + y.val;
    ret.len = y.len + x.len;
    for ( int i = 1;i < len;i++ ) {
        if ( x.r[i] * po[len - i] + y.l[len - i] == has ) {
            ret.val++;
        }
    }
    for ( int i = 1;i < len;i++ ) {
        if ( i <= x.len ) {
            ret.l[i] = x.l[i];
        }
        else if ( i <= ret.len ) {
            ret.l[i] = x.l[x.len] * po[i - x.len] + y.l[i - x.len];
        }
        else {
            ret.l[i] = 0;
        }
    }
    for ( int i = 1;i < len;i++ ) {
        if ( i <= y.len ) {
            ret.r[i] = y.r[i];
        }
        else if ( i <= ret.len ) {
            ret.r[i] = x.r[i - y.len] * po[y.len] + y.r[y.len];
        }
        else {
            ret.r[i] = 0;
        }
    }
    return ret;
}

void pushup(int p)
{
    tree[p][0] = combine(tree[lson][0], tree[rson][0]);
    tree[p][1] = combine(tree[rson][1], tree[lson][1]);
}

void build(int l = 1, int r = n, int p = 1)
{
    tree[p][0].len = tree[p][1].len = r - l + 1;
    if ( l == r ) {
        tree[p][0].l[1] = tree[p][0].r[1] = a[id[l]];
        tree[p][1].l[1] = tree[p][1].r[1] = a[id[r]];
        tree[p][0].val = tree[p][1].val = (tree[p][0].l[1] == has);
        return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, lson);
    build(mid + 1, r, rson);
    pushup(p);
}

void update(int x, char ch, int l = 1, int r = n, int p = 1)
{
    if ( l == r ) {
        tree[p][0].l[1] = tree[p][0].r[1] = ch;
        tree[p][1].l[1] = tree[p][1].r[1] = ch;
        tree[p][0].val = tree[p][1].val = (tree[p][0].l[1] == has);
        return;
    }
    int mid = (l + r) >> 1;
    if ( x <= mid ) {
        update(x, ch, l, mid, lson);
    }
    else update(x, ch, mid + 1, r, rson);
    pushup(p);
}

Tree query(int s, int t, int k, int l = 1, int r = n, int p = 1)
{
    if ( s <= l && r <= t ) {
        return tree[p][k];
    }
    int mid = (l + r) >> 1;
    Tree ret {};
    if ( k == 0 ) {
        if ( s <= mid ) {
            ret = combine(query(s, t, k, l, mid, lson), ret);
        }
        if ( t > mid ) {
            ret = combine(ret, query(s, t, k, mid + 1, r, rson));
        }
    }
    else {
        if ( s <= mid ) {
            ret = combine(ret, query(s, t, k, l, mid, lson));
        }
        if ( t > mid ) {
            ret = combine(query(s, t, k, mid + 1, r, rson), ret);
        }
        // if ( t > mid ) {
        //     ret = combine(ret, query(s, t, k, mid + 1, r, rson));
        // }
        // if ( s <= mid ) {
        //     ret = combine(ret, query(s, t, k, l, mid, lson));
        // }
    }
    return ret;
}

int qpahth(int x, int y)
{
    Tree ret1 {}, ret2 {};
    while ( top[x] != top[y] ) {
        if ( dep[top[x]] >= dep[top[y]] ) {
            ret1 = combine(ret1, query(dfn[top[x]], dfn[x], 1));
            x = fa[top[x]];
        }
        else {
            ret2 = combine(query(dfn[top[y]], dfn[y], 0), ret2);
            y = fa[top[y]];
        }
    }
    if ( dep[x] >= dep[y] ) {
        ret1 = combine(ret1, query(dfn[y], dfn[x], 1));
    }
    else {
        ret2 = combine(query(dfn[x], dfn[y], 0), ret2);
    }
    Tree ret = combine(ret1, ret2);
    return ret.val;
}

int main()
{
    po[0] = 1;
    for ( int i = 1;i <= 1e5;i++ ) {
        po[i] = po[i - 1] * 19260817;
        head[i] = -1;
    }

    scanf("%d%d", &n, &q);
    scanf("%s", s + 1);
    len = strlen(s + 1);
    for ( int i = 1;i <= len;i++ ) {
        has = has * 19260817 + s[i];
    }
    scanf("%s", a + 1);
    for ( int i = 1;i <= n - 1;i++ ) {
        int u, v;
        scanf("%d%d", &u, &v);
        addedge(u, v);
        addedge(v, u);
    }
    dfs1();
    dfs2();
    build();
    int opt;
    for ( int i = 1;i <= q;i++ ) {
        int u, v;
        scanf("%d", &opt);
        if ( opt == 1 ) {
            scanf("%d%d", &u, &v);
            printf("%dn", qpahth(u, v));
        }
        else {
            char ch[3];
            scanf("%d%s", &u, ch + 1);
            update(dfn[u], ch[1]);
        }
    }
    return 0;
}
I.Switches

显然最多操作两轮,只需要 n 2 n^2 n2模拟即可。

#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 1e3 + 10;
int a[maxn];
vector vec[maxn];
int n, m;
int l;

int main()
{
    scanf("%d%d", &n, &m);
    scanf("%d", &l);
    if ( l == 0 ) {
        printf("0n");
        return 0;
    }
    for ( int i = 1;i <= l;i++ ) {
        int x;
        scanf("%d", &x);
        a[x] = 1;
    }
    for ( int i = 1;i <= n;i++ ) {
        int k;
        scanf("%d", &k);
        vec[i].resize(k);
        for ( int j = 0;j <= k - 1;j++ ) {
            int x;
            scanf("%d", &x);
            vec[i][j] = x;
        }
    }
    int ans = 0;
    for ( int i = 1;i <= n;i++ ) {
        ans++;
        for ( int j = 0;j < vec[i].size();j++ ) {
            a[vec[i][j]] ^= 1;
        }
        int flag = 0;
        for ( int j = 1;j <= n;j++ ) {
            if ( a[j] != 0 ) {
                flag = 1;
            }
        }
        if ( flag == 0 ) {
            printf("%dn", ans);
            return 0;
        }
    }
    for ( int i = 1;i <= n;i++ ) {
        ans++;
        for ( int j = 0;j < vec[i].size();j++ ) {
            a[vec[i][j]] ^= 1;
        }
        int flag = 0;
        for ( int j = 1;j <= n;j++ ) {
            if ( a[j] != 0 ) {
                flag = 1;
            }
        }
        if ( flag == 0 ) {
            printf("%dn", ans);
            return 0;
        }
    }
    printf("-1n");
    return 0;
}
J.Joining Capitals

一道裸的斯坦纳树,要求关键点的度数为1。

在转移的时候不进行以关键点为中间点的转移即可。

#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 1e2 + 10;
int n, k;
struct TT {
    int x, y;
}p[maxn];
struct Edge {
    double val;
    int to, next;
}e[maxn * maxn << 1];
int head[maxn], tot;
double dp[maxn][2000];
int key[maxn];
int vis[maxn];

void addedge(int u, int v, double w)
{
    e[++tot] = Edge({ w,v,head[u] });
    head[u] = tot;
}

double dist(int x, int y)
{
    return sqrt(1.0 * (p[x].x - p[y].x) * (p[x].x - p[y].x) + (p[x].y - p[y].y) * (p[x].y - p[y].y));
}

void spfa(int s)
{
    queue qq;
    for ( int i = 0;i <= n - 1;i++ ) {
        if ( dp[i][s] != 0x3f3f3f3f ) {
            qq.push(i);
            vis[i] = 1;
        }
    }
    while ( !qq.empty() ) {
        int temp = qq.front();
        qq.pop();
        vis[temp] = 0;
        for ( int i = head[temp];~i;i = e[i].next ) {
            int to = e[i].to;
            if ( dp[to][s] > dp[temp][s] + e[i].val ) {
                dp[to][s] = dp[temp][s] + e[i].val;
                if ( !vis[to] ) {
                    vis[to] = 1;
                    qq.push(to);
                }
            }
        }
    }
}

int main()
{
    scanf("%d%d", &n, &k);
    for ( int i = 0;i <= n - 1;i++ ) {
        scanf("%d%d", &p[i].x, &p[i].y);
        head[i] = -1;
    }
    for ( int i = 0;i <= n - 1;i++ ) {
        for ( int j = 0;j < i;j++ ) {
            addedge(i, j, dist(i, j));
            addedge(j, i, dist(i, j));
        }
    }
    for ( int i = 0;i <= n - 1;i++ ) {
        for ( int j = 0;j < (1 << k);j++ ) {
            dp[i][j] = 0x3f3f3f3f;
        }
    }
    for ( int i = 0;i <= k - 1;i++ ) {
        dp[i][1 << i] = 0;
    }
    for ( int s = 1;s < (1 << k);s++ ) {
        for ( int i = k;i <= n - 1;i++ ) {
            for ( int subs = (s - 1) & s;subs;subs = (subs - 1) & s ) {
                dp[i][s] = min(dp[i][s], dp[i][subs] + dp[i][s ^ subs]);
            }
        }
        spfa(s);
    }
    double ans = 0x3f3f3f3f;
    for ( int i = 0;i <= n - 1;i++ ) {
        ans = min(ans, dp[i][(1 << k) - 1]);
    }
    printf("%.5fn", ans);
    return 0;
}
K.Kepler

给定若干个包含原点的圆,不会有三个圆交在同一点,求这些圆的交点数,若超过 2 n 2n 2n个则输出 g r e a t e r greater greater

由于都包含原点,那么圆和圆之间要么是包含关系,要么是相交关系。那么就转化为求相交的圆的个数。

注意到圆心范围很小,我们可以将圆划分为若干个集合,每个集合里的圆都两两不相交,这样最多划分为 50 ∗ 50 50*50 50∗50个集合。

我们按照半径从小到大加入圆,枚举每一个集合,判断与该集合最外层的圆是否相交,如果相交,那么从大到小枚举该集合内的圆,逐个判断是否相交,一旦不相交就不再继续枚举。枚举结束后,选择一个不相交的集合加入其中,若不存在则放入一个新的集合中。

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 2e5 + 10;
struct TT {
    double x, y, r;
    int id;
    bool operator<(const TT& t)const
    {
        return r < t.r;
    }
}a[maxn];
struct Edge {
    int to, next;
}e[maxn << 1];
int head[maxn], tot;
int n;
set st;
int ans = 0;

void addedge(int u, int v)
{
    e[++tot] = Edge({ v,head[u] });
    head[u] = tot;
}

bool check(int x, int y)
{
    double t1 = (a[x].x - a[y].x) * (a[x].x - a[y].x) + (a[x].y - a[y].y) * (a[x].y - a[y].y);
    double t2 = (a[x].r - a[y].r) * (a[x].r - a[y].r);
    return t1 <= t2;
}

void solve(int tt,int now)
{
    if ( check(tt, now) ) {
        return;
    }
    ans++;
    for ( int i = head[now];~i;i = e[i].next ) {
        int to = e[i].to;
        solve(tt, to);
    }
}

int main()
{
    scanf("%d", &n);
    for ( int i = 1;i <= n;i++ ) {
        scanf("%lf%lf%lf", &a[i].x, &a[i].y, &a[i].r);
        a[i].id = i;
    }
    for ( int i = 1;i <= n;i++ ) {
        head[i] = -1;
    }
    sort(a + 1, a + n + 1);
    for ( int i = 1;i <= n;i++ ) {
        set::iterator tt;
        int temp = 0;
        for ( auto j = st.begin();j != st.end();j++ ) {
            if ( check(i, *j) ) {
                temp = *j;
                tt = j;
            }
            else {
                solve(i, *j);
            }
        }
        if ( ans > n ) {
            break;
        }
        st.insert(i);
        if ( !temp ) {
            continue;
        }
        addedge(i, temp);
        st.erase(tt);
    }
    if ( ans > n ) {
        printf("greatern");
    }
    else {
        printf("%dn", ans * 2);
    }
    return 0;
}
L.Subway Lines

树剖板子题,对一条路径上的点置为1,然后对另外一条路径进行查询。

#include 
#include 
#include 
#include 
#include 
#define lson ( p << 1 )
#define rson ( p << 1 | 1 )
using namespace std;
const int maxn = 2e5 + 10;
struct Edge {
    int to, next;
}e[maxn << 1];
int head[maxn], tot;
struct Tree {
    int val;
    int lazy;
}tree[maxn << 2];
int n, q;
int dep[maxn], top[maxn], siz[maxn], fa[maxn], son[maxn];
int id[maxn], wt[maxn];
int cnt;

void addedge(int u, int v)
{
    e[++tot] = Edge({ v,head[u] });
    head[u] = tot;
}

void dfs1(int now = 1, int last = 0, int deep = 1)
{
    fa[now] = last;
    dep[now] = deep;
    siz[now] = 1;
    int maxson = -1;
    for ( int i = head[now];~i;i = e[i].next ) {
        int to = e[i].to;
        if ( to == last )
            continue;
        dfs1(to, now, deep + 1);
        siz[now] += siz[to];
        if ( siz[to] > maxson ) {
            maxson = siz[to];
            son[now] = to;
        }
    }
}

void dfs2(int now = 1, int topf = 1)
{
    id[now] = ++cnt;
    top[now] = topf;
    if ( !son[now] )
        return;
    dfs2(son[now], topf);
    for ( int i = head[now];~i;i = e[i].next ) {
        int to = e[i].to;
        if ( to == fa[now] || to == son[now] )
            continue;
        dfs2(to, to);
    }
}

void pushdown(int p, int l = 1,int r = n)
{
    int mid = (l + r) >> 1;
    tree[lson].val += (mid - l + 1) * tree[p].lazy;
    tree[rson].val += (r - mid) * (tree[p].lazy);
    tree[lson].lazy += tree[p].lazy;
    tree[rson].lazy += tree[p].lazy;
    tree[p].lazy = 0;
}

void update(int s, int t, int c, int l = 1, int r = n, int p = 1)
{
    if ( s <= l && r <= t ) {
        int tt = (r - l + 1) * c;
        tree[p].val += tt;
        tree[p].lazy += c;
        return;
    }
    int mid = (l + r) >> 1;
    if ( tree[p].lazy )
        pushdown(p, l, r);
    if ( s <= mid )
        update(s, t, c, l, mid, lson);
    if ( t > mid )
        update(s, t, c, mid + 1, r, rson);
    tree[p].val = tree[lson].val + tree[rson].val;
}

int query(int s, int t, int l = 1, int r = n, int p = 1)
{
    if ( s <= l && r <= t ) {
        return tree[p].val;
    }
    int mid = (l + r) >> 1;
    if ( tree[p].lazy )
        pushdown(p, l, r);
    int ret = 0;
    if ( s <= mid )
        ret += query(s, t, l, mid, lson);
    if ( t > mid )
        ret += query(s, t, mid + 1, r, rson);
    return ret;
}

int qpath(int x, int y)
{
    int ret = 0;
    while ( top[x] != top[y] ) {
        if ( dep[top[x]] < dep[top[y]] )
            swap(x, y);
        ret += query(id[top[x]], id[x]);
        x = fa[top[x]];
    }
    if ( dep[x] > dep[y] )
        swap(x, y);
    ret += query(id[x], id[y]);
    return ret;
}

void updpath(int x, int y, int c)
{
    while ( top[x] != top[y] ) {
        if ( dep[top[x]] < dep[top[y]] )
            swap(x, y);
        update(id[top[x]], id[x], c);
        x = fa[top[x]];
    }
    if ( dep[x] > dep[y] )
        swap(x, y);
    update(id[x], id[y], c);
}

int main()
{
    scanf("%d%d", &n, &q);
    memset(head, -1, sizeof head);
    for ( int i = 1;i <= n - 1;i++ ) {
        int u, v;
        scanf("%d%d", &u, &v);
        addedge(u, v);
        addedge(v, u);
    }
    dfs1();
    dfs2();
    for ( int i = 1;i <= q;i++ ) {
        int a, b, c, d;
        scanf("%d%d%d%d", &a, &b, &c, &d);
        updpath(a, b, 1);
        int tt = qpath(c, d);
        printf("%dn", tt);
        updpath(a, b, -1);
    }
    return 0;
}
M.Modifying SAT

可以转化为异或方程组,用高斯消元求解。由于要求字典序最大,我们选择从后向前消元,使自由元尽可能靠前,这样可以使得答案字典序最大。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define debug(x) cout << #x << " : " << (x) << "n"
using namespace std;
const int maxn = 2e3 + 10;
int m, n;
bitset bt[maxn];
int ans[maxn];
int anss;

void guass()
{
    for ( int i = 1, now = 1;now <= n;now++ ) {
        int pos = 0;
        for ( int j = i;j <= m;j++ ) {
            if ( bt[j][now] ) {
                pos = j;
                break;
            }
        }
        if ( !pos ) {
            ans[n + 1 - now] = 1;
            continue;
        }
        swap(bt[pos], bt[i]);
        for ( int j = 1;j <= m;j++ ) {
            if ( j != i && bt[j][now] ) {
                bt[j] ^= bt[i];
            }
        }
        i++;
    }
    for ( int i = 1;i <= m;i++ ) {
        if ( bt[i].count() == 1 && bt[i][n + 1] == 1 ) {
            anss = -1;
            return;
        }
    }
    for ( int i = 1;i <= m;i++ ) {
        if ( bt[i].count() == 0 ) {
            continue;
        }
        int tt = bt[i]._Find_first();
        ans[n + 1 - tt] = bt[i][n + 1];
        for ( int j = tt + 1;j <= n;j++ ) {
            ans[n + 1 - tt] = ans[n + 1 - tt] ^ bt[i][j];
        }
    }
    return;
}

int main()
{
    scanf("%d%d", &m, &n);
    char s[maxn];
    for ( int i = 1;i <= m;i++ ) {
        int flag = 0;
        bt[i][n + 1] = 1;
        while ( 1 ) {
            scanf("%s", s + 1);
            int ttt = 1;
            if ( s[1] == '(' ) {
                ttt++;
            }
            if ( s[ttt] == 'a' ) {
                break;
            }
            if ( s[ttt] == 'o' ) {
                flag = 0;
                continue;
            }
            if ( s[ttt] == 'n' ) {
                flag = 1;
                continue;
            }
            int len = strlen(s + 1);
            int tt = 0;
            int id = 0;
            for ( int j = 1;j <= len;j++ ) {
                if ( tt && s[j] >= '0' && s[j] <= '9') {
                    id = id * 10 + s[j] - '0';
                }
                else {
                    if ( s[j] >= '0' && s[j] <= '9' ) {
                        tt = 1;
                        id = s[j] - '0';
                    }
                }
            }
            bt[i][n + 1] = bt[i][n + 1] ^ flag;
            bt[i][n + 1 - id] = bt[i][n + 1 - id] ^ 1;
            if ( i == m && s[len] == ')' ) {
                break;
            }
        }
    }
    guass();
    if ( anss == -1 ) {
        printf("impossiblen");
    }
    else {
        for ( int i = 1;i <= n;i++ ) {
            if ( ans[i] ) {
                printf("T");
            }
            else printf("F");
        }
    }
    return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/868967.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号