首页 > 其他分享 >2022杭电多校8

2022杭电多校8

时间:2022-09-24 18:12:04浏览次数:57  
标签:杭电多校 题意 int ll 2022 include void dp

A. Theramore

题意:

给定一个01串,可以选择一个奇数长度的区间,使得该区间翻转,求任意次数操作后的最小字典序。

分析:

我们发现不管怎么转,奇数位置上的数永远在奇数上,偶数永远还在偶数上,但是我们可以通过翻转随意的去更换位置,因此我们只需要统计处奇偶上01的位

置,然后把1尽可能的往后排即可。

#include <iostream>
#include <cstring>

using namespace std;

char s[100010];
int c[2][2];

int main() 
{
    int T;
    scanf("%d", &T);
    while (T--) 
    {
        memset(c, 0, sizeof c);
        scanf("%s", s + 1);
        for (int i = 1; s[i]; i++) 
            c[s[i] - '0'][i & 1]++;
        for (int i = 1; s[i]; i++) 
        {
            if (c[0][i & 1]) c[0][i & 1]--, printf("0");
            else printf("1");
        }
        puts("");
    }
    return 0;
}

D. Quel'Thalas

题意:

求出最少的线条覆盖n*n的矩阵。

分析:

横竖都来n条,输出2n即可。

#include <iostream>

using namespace std;

int main() 
{
    int T;
    cin >> T;
    while (T--) 
    {
        int n;
        cin >> n;
        cout << (n << 1) << '\n';
    }
    return 0;
}

G. Darnassus

题意:

给定一个长度为n的序列a,是有1-n的排列组成,连接i j两个点的代价为abs(i - j) * abs(a[i] - a[j]),求其最小生成树。

分析:

这个题的思考方式很独特

如果我们依次连接 i 和 i+1 会发现 abs(i-j) 这一项就消掉了

因为序列a是排列 所以 abs(a[i]-a[j])≤n-1

也就是说只需要知道边权≤n-1即可

|a[i]-a[j]|*|i-j|≤n-1

我们每个点 i 开始枚举向后扩展sq=sqrt(n)这样可以使得 |i-j|≤sq

for (int i = 1; i <= n; i++) {
    for (int j = i + 1; j <= n && j <= i + sq; j++) {
        int w = abs(i - j) * abs(p[i] - p[j]);
        if (w < n)e[w].push_back({ i, j });
    }
}

但是也有可能|i-j|≥sq |a[i]-a[j]|≤sq的情况

同样需要枚举 |a[i]-a[j]|≤sq

for (int i = 1; i <= n; i++) {
    for (int j = i + 1; j <= n && j <= i + sq; j++) {
        int w = abs(i - j) * abs(pos[i] - pos[j]);//pos[p[i]] = i
        if (w < n)e[w].push_back({ pos[i], pos[j] });
    }
}

复杂度为O(n sqrt(n)) 再排个序就会炸掉 所以用个捅 储存边权等于w的所有边

namespace UF {

    int fa[MAXN], rank[MAXN];
    inline void init(int n)
    {
        for (int i = 1; i <= n; ++i)
        {
            fa[i] = i;
            rank[i] = 1;
        }
    }
    int find(int x)
    {
        return x == fa[x] ? x : (fa[x] = find(fa[x]));
    }
    inline void merge(int i, int j)
    {
        int x = find(i), y = find(j);
        if (rank[x] <= rank[y])
            fa[x] = y;
        else
            fa[y] = x;
        if (rank[x] == rank[y] && x != y)
            rank[y]++;
    }
    inline bool same(int i, int j) {
        return find(i) == find(j);
    }
}
int n;
int p[MAXN], pos[MAXN];
void slove() {
    cin >> n;
    UF::init(n);
    for (int i = 1; i <= n; i++)cin >> p[i], pos[p[i]] = i;
    vector<vector<pair<int, int>>>e(n);
    for (int i = 2; i <= n; i++) {
        int w = abs(p[i] - p[i - 1]);
        e[w].push_back({ i - 1,i });
    }
    int sq = sqrt(n);
    for (int i = 1; i <= n; i++) {
        for (int j = i + 1; j <= n && j <= i + sq; j++) {
            int w = abs(i - j) * abs(p[i] - p[j]);
            if (w < n)e[w].push_back({ i, j });
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = i + 1; j <= n && j <= i + sq; j++) {
            int w = abs(i - j) * abs(pos[i] - pos[j]);
            if (w < n)e[w].push_back({ pos[i], pos[j] });
        }
    }
    ll ans = 0;
    int cnt = n;
    for (int w = 0; w <= n - 1; w++) {
        for (auto p : e[w]) {
            int u = p.first, v = p.second;
            if (UF::same(u, v))continue;
            UF::merge(u, v);
            ans += w;
            cnt--;
            if (cnt == 1)goto END;
        }
    }
END:

    cout << ans << endl;
}

H. Orgrimmar

题意:

给定一颗树,给你选择一些点,使得这些点,最多只有两个点直接有边相连。

做法:

显然的树上dp [树的最大独立集]

但是这个题目又有一点不一样 可以选最多一对相邻的两个点

所以dp也就要改变一点

f[u][0]表示u点没选

f[u][1]表示u点选了 并且子节点没有与之相连的点

f[u][2]表示u点选了 但是子节点中有与之相连的点

转移过程有点像皇宫看守

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t,n;
const int N=5e5+10;
ll dp[N][3];
int h[N],e[N],ne[N],idx;
void add(int a,int b){
	e[++idx]=b;
	ne[idx]=h[a];
	h[a]=idx;
}
void dfs(int u,int fa){
	ll m0=0,m1=0,m2=-1e9;
	for(int i=h[u];i!=-1;i=ne[i]){
		int j=e[i];
		if(j==fa) continue;
		dfs(j,u);
		m0+=max({dp[j][0],dp[j][1],dp[j][2]});
		m1+=dp[j][0];
		m2=max(dp[j][1]-dp[j][0],m2);
	}
	dp[u][0]=m0;
	dp[u][1]=m1+1;
	dp[u][2]=m1+m2+1;
}
void solve(){
	scanf("%d",&n);
	memset(h,-1,sizeof h);
	memset(dp,0,sizeof dp);
	idx=0;
	for(int i=1;i<n;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		add(a,b),add(b,a);
	}
	dfs(1,0);
	printf("%d\n",max({dp[1][0],dp[1][1],dp[1][2]}));
}
int main()
{
	int size(512<<20); // 512M
	__asm__ ( "movq %0, %%rsp\n"::"r"((char*)malloc(size)+size));
    
    scanf("%d",&t);
    while(t--){
    	solve();
    }
    exit(0);
}

K. Stormwind

题意:

给定一个n*m的矩阵,要在其中画尽可能地线。

线满足地条件:

1、线的端点必须在矩阵地边界上。

2、线必须平行于至少一条边。

3、在画完后,每一块需为面积大于等于k的矩阵。

4、线不能有共同端点。

分析:

可以枚举切出来的矩阵的宽度,再算长度。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,k,t;
void solve(){
    cin>>n>>m>>k;
    ll ans=0;
    for(int i=1;i<=n&&i<=k;i++){
        ll c;
        if(k%i) c=k/i+1;
        else c=k/i;
        ll cnt=0;
        if(m>=c){
            cnt+=m/c-1;
            cnt+=n/i-1;
            ans=max(ans,cnt);
            
        }
        
    }
    cout<<ans<<endl;
}
int main()
{
    cin.tie(0);
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--){
        solve();
    }
}

标签:杭电多校,题意,int,ll,2022,include,void,dp
From: https://www.cnblogs.com/wzxbeliever/p/16726134.html

相关文章