【题目记录】——第 46 届 ICPC 国际大学生程序设计竞赛亚洲区域赛(沈阳)

news/2024/7/20 20:24:21 标签: 图论, 算法, 深度优先

文章目录

  • B Bitwise Exclusive-OR Sequence
  • E Edward Gaming, the Champion
  • J Luggage Lock
  • H Line Graph Matching
  • M String Problem 字符串,后缀自动机

题目地址 第 46 届 ICPC 国际大学生程序设计竞赛亚洲区域赛(沈阳)

B Bitwise Exclusive-OR Sequence

题目地址B Bitwise Exclusive-OR Sequence
题目大意:给你一个n以及m,n代表变量的个数,m代表限制方程的个数,接下来给出m行,每行3个数a,b,c代表第a个变量和第b个变量的异或值为c,问n个变量的最小和是多少,如果异或方程出现矛盾,则直接输出-1。
思路:首先可能是多个图, 对于每个图中的每一位, 取0 或者取1 都是可以确定图上的其他数字取0 或者取1 。 所以我们可以去枚举取0 或者取1 ,来取个min 得到结果。
所以这里我们用并查集, 最大数为 2的30次, 所以我们对于每一位都做一个并查集, 就是30个并查集。 f[a] 表示当前位置取1 f[a+n] 表示当前位取0
这样就可以枚举区间了
AC代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;

const int N = 2e5+10;

LL n, m, fa[N], sz[N];
LL a[N];

struct Node
{
	LL a, b;
	LL val;
}num[N];

LL find(LL x)
{
	if(fa[x] != x) fa[x] = find(fa[x]);
	return fa[x];
}


void init()
{
	for(LL i = 1; i <= n; i ++)
	{
		fa[i] = i, sz[i] = 1;
		fa[i+n] = i+n, sz[i+n] = 0;
	}
}

void merge(LL a, LL b)
{
	sz[a] += sz[b];
	fa[b] = a;
}

int main()
{
	cin >> n >> m;
	for(int i = 1; i <= m; i ++)
	{
		scanf("%lld%lld%lld", &num[i].a, &num[i].b, &num[i].val);
	}
	
	LL res = 0;
	
	for(int i = 0; i < 30; i ++)
	{
		init(); // 每一次做并查集都要初始化!
		for(int j = 1; j <= m; j ++)
		{
			int aa = find(num[j].a);
			int bb = find(num[j].b);
			
			int aaa = find(num[j].a + n);
			int bbb = find(num[j].b + n);
			
			if((num[j].val >> i) & 1 ) // 不同为1 ,一个取0一个取1
			{
				if(aa == bb)
				{
					cout << -1 << endl;
					return 0;
				}
				if(aa == bbb) continue;
				
				merge(aa, bbb);
				merge(bb, aaa);
			}
			else // 相同为0, 都取1 或者都取0
			{
				if(aa == bbb )
				{
					cout << -1 << endl;
					return 0;
				}
				if(aa == bb) continue;
				
				merge(bb, aa);
				merge(bbb, aaa);
			}
		}
		for(int j = 1; j <= n; j ++) // 对于每个数字计算结果
		{
			res += (LL)min(sz[find(j)], sz[find(j+n)]) * (1 << i);
			sz[find(j)] = 0;
			sz[find(j+n)] = 0;
		}
	}
	
	cout << res << endl;
	return 0;
}

E Edward Gaming, the Champion

题目地址 E Edward Gaming, the Champion
题目大意:给出一个很长的字符串,判断"edgnb"在里面出现了几次

思路:KMP模板,直接套就行
AC代码:

#include <bits/stdc++.h>
#define int long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=2e5+10;
int ans,P[maxn];
char A[maxn],B[maxn]= {"\0edgnb"};
//这里的A和B串以1开始存储
void Pre() {
    int j=0,m=strlen(B+1);
    P[1]=0;
    for(int i=1; i<m; i++) {
        while(j>0&&B[i+1]!=B[j+1])
            j=P[j];
        if(B[i+1]==B[j+1])
            j++;
        P[i+1]=j;
    }
}
int KMP() {
    int j=0,n=strlen(A+1),ans=0,m=strlen(B+1);
    for(int i=0; i<n; i++) {
        while(j>0&&A[i+1]!=B[j+1])
            j=P[j];
        if(A[i+1]==B[j+1])
            j++;
        if(j==m) {//匹配成功,记录数量并且清零
            ans++;
            j=0;
        }
    }
    return ans;
}
signed main() {
    scanf("%s",A+1);
    Pre();
    cout <<KMP();
    return 0;
}

J Luggage Lock

题目地址J Luggage Lock

题目大意:给出一个4位密码锁,每次只能一次性多个滑动相邻的位置向上或向下移动一格,现在给出多次询问,每次询问给出一个初始值和终点值,询问两者间的最少操作数

思路:可以观察到,无论初始点和终点值是多少,如aaaa,bbbb,最后都可以等效成从0000 到(b-a)(b-a)(b-a)(按位操作,取模),那么从0000点开始求出其到其他所有状态的操作数并记录,直接BFS,对于给出的询问直接等效成从0000出发即可
AC代码

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=1e5+5;
vector<int> mm[N];
int dis[N];
bool flag[N];
void init(int n)
{
    int a[4]={0},b[4];
    int cnt = 0;
    int x = n;
    while(n)
    {
        a[cnt]=n%10;
        n/=10;
        cnt++;
    }
    int t = 0;
    for(int i = 1;i <= 4;i++)
    {
        for(int j = 0;j < 5-i;j++)
        {
            for(int k = 0;k < 4;k ++)
            {
                b[k]=a[k];
            }
            for(int k = j;k < j+i;k++)
            {
                b[k]=(a[k]+1)%10;
            }
            for(int tt = 3;tt >= 0;tt--)
            {
                t=t*10+b[tt];
            }
            mm[x].push_back(t);
            t = 0;
            for(int k = j;k < j+i;k++)
            {
                b[k]=(a[k]-1+10)%10;
            }
            for(int tt = 3;tt >= 0;tt--)
            {
                t=t*10+b[tt];
            }
            mm[x].push_back(t);
            t = 0;
        }
    }
}
void BFS()
{
    int d=1;
    queue<int> q;
    q.push(0);
    while(!q.empty())
    {
        int leng = q.size();
        while(leng--)
        {
            int t = q.front();
            q.pop();
            int n = mm[t].size();
            for(int i = 0;i < n;i++)
            {
                if(!flag[mm[t][i]])
                {
                    dis[mm[t][i]]=d;
                    q.push(mm[t][i]);
                    flag[mm[t][i]]=true;
                }
            }
        }
        d++;
    }
}
void solve()
{
    int a,b;
    scanf("%d%d",&a,&b);
    if(a==b)
    {
        printf("0\n");
        return;
    }
    int aa[4]={},bb[4]={},cc[4];
    int cnt=0;
    while(a)
    {
        aa[cnt++]=a%10;
        a/=10;
    }
    cnt=0;
    while(b)
    {
        bb[cnt++]=b%10;
        b/=10;
    }
    for(int i = 0;i < 4;i++)
    {
        cc[i]=(bb[i]-aa[i]+10)%10;
    }
    int t = 0;
    for(int i = 3;i >=0;i--)
    {
        t=t*10+cc[i];
    }
    printf("%d\n",dis[t]);
}

int main()
{
//	freopen("in.txt","r",stdin);
//	int t = 1;
	int t;
	scanf("%d",&t);
	for(int i = 0;i<=9999;i++)
    {
        init(i);
    }
    BFS();
	while(t--)
	{
		solve();
	}
    return 0;
}

H Line Graph Matching

题目地址H Line Graph Matching
给定一个无向带权图,将其所有的 边看作点,点看作边 构建出新图也是一个无向带权图(两点之间的边权为这两点的权值(原边权)相加),求这个新图上的最大独立边集 —— 选取所有 不相邻 的边构成的集合保证权值累加Max

思路:

  • 首先需要理解的是,选取新图任意一条边 == 选取原图中的两条相邻边(权值);
  • 若选取新图中两条相邻边,它们肯定会共享一个点,这个点相当于原图的同一条边,会产生交集;
  • 题意是不能选取相邻边的,所以我们在 新图要求的最大独立边集 == 原图中选取不重复相邻边配对,尽可能权值和Max
  • 细节分析
    AC代码:
#include<bits/stdc++.h>
#include<unordered_set>
#include<unordered_map>
#define mem(a,b) memset(a,b,sizeof a)
#define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define sca scanf
#define pri printf
#define ul (u << 1)
#define ur (u << 1 | 1)
#define forr(a,b,c) for(int a=b;a<=c;a++)
#define rfor(a,b,c) for(int a=b;a>=c;a--)
using namespace std;

typedef long long ll;
typedef pair<int, int> PII;
typedef unsigned long long ull;

const int N = 100010, M = 200010, MM = N;
int INF = 0x3f3f3f3f, mod = 1e9 + 7;
ll LNF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, T, S, D;
int p[N], f[N];
struct edge
{
	int a, b, x;
	bool operator<(const edge& ee)const { return x > ee.x; }
}ed[M];

int find(int x) {
	if (p[x] != x)p[x] = find(p[x]);
	return p[x];
}

int main() {
	cinios;

	cin >> n >> m;
	forr(i, 1, n)p[i] = i;

	forr(i, 1, m) {
		int a, b, x;
		cin >> a >> b >> x;
		ed[i] = { a,b,x };
	}
	sort(ed + 1, ed + 1 + m);//贪心每次选取尽可能大的边去配对

	ll ans = 0;//配对成功一对边我们就可以累计在贡献里
	forr(i, 1, m) {
		int a = ed[i].a, b = ed[i].b, w = ed[i].x;
		a = find(a), b = find(b);

		if (a == b) { //若在同一连通块
			if (!f[a])f[a] = w;//无配对
			else ans += w + f[a], f[a] = 0;
			//有配对就配
		}
		else { 
			if (!f[a] && !f[b])f[b] = f[a] = w;//两边都无配对,现有的边可以放此块中等候配对
			else { //否则只要有一边有配对
				ans += w + max(f[a], f[b]); //显然是优先和大的配
				f[a] = f[b] = min(f[a], f[b]);//小的继续等候
			}
			p[a] = b;
		}
	}
	cout << ans;

	return 0;
}

M String Problem 字符串,后缀自动机

题目地址M String Problem
题目大意:输出字典序最小最大开始的序列编号以及出现过几次;
思路:遇到字典序问题,考虑利用后缀自动机来解.
静态问题可以沿着后缀自动机的字符边贪心走大的.
那么不妨先维护出前缀 i 的答案,然后考虑下一位的影响.
显然,如果影响的话一定是下一位的字母被加入答案,那么在后缀自动机中就是答案中深度最小的点改变.
改变成的新点一定是加入 i+1 字符时所影响的新点,而所有新点总和是 O(n) 的.
由于后缀自动机的构建方式,在线做是不现实的,不妨离线构建完后缀自动机然后记录每个点最早出现位置.
最后开一个数组统计并离线下来即可.
AC代码:

#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define N  2000009 
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;  
char str[N];
int n, last, tot, fir[N], len[N], st[N], ch[N][26], pre[N], dep[N], vis[N], tail, a[N];  
struct data {
    int p, c; 
    data(int p = 0, int c = 0):p(p), c(c){} 
};
vector<data>G[N];
void extend(int c, int pos) {     
    int np = ++ tot, p = last;
    len[np] = len[p] + 1, last = np;  
    fir[np] = pos;  
    for(; p && !ch[p][c]; p = pre[p]) {
        ch[p][c] = np;                   
    }
    if(!p) pre[np] = 1;
    else {
        int q = ch[p][c];
        if(len[q] == len[p] + 1) pre[np] = q;
        else {
            int nq = ++ tot; 
            len[nq] = len[p] + 1;
            fir[nq] = fir[q];
            memcpy(ch[nq], ch[q], sizeof(ch[q]));       
            pre[nq] = pre[q], pre[np] = pre[q] = nq;
            for(; p && ch[p][c] == q; p = pre[p])
                ch[p][c] = nq; 
        }
    }
}   
int main() {
    // setIO("input");
    scanf("%s", str + 1);
    n = strlen(str + 1);    
    last = tot = 1;
    for(int i = 1; i <= n ; ++ i) {   
        extend(str[i] - 'a', i);
    } 
    for(int i = 1; i <= tot; ++ i) {  
        for(int j = 0; j < 26; ++ j) {
            if(ch[i][j]) {
                int q = ch[i][j];
                G[fir[q]].pb(data(i, j));   
            }
        }
    }     
    vis[1] = 1, a[++ tail] = 1, st[1] = -1;      
    for(int i = 1; i <= n ; ++ i) {
        int mk = 0, de = 0;            
        for(int j = 0; j < G[i].size() ; ++ j) {
            data e = G[i][j];                 
            if(vis[e.p] && e.c > st[e.p]) {     
                if(mk == 0) {
                    mk = e.p, de = dep[e.p];
                }
                else if(dep[e.p] < de) {
                    mk = e.p, de = dep[e.p];  
                }
            }
        }    
        if(mk) {
            while(a[tail] != mk)
                vis[a[tail]] = 0, -- tail;    
            st[mk] = str[i] - 'a';   
            a[++ tail] = ch[mk][str[i] - 'a'], dep[a[tail]] = tail, vis[a[tail]] = 1, st[a[tail]] = -1;  
        }            
        printf("%d %d\n", i - tail + 2, i);
    }
    return 0;
}

http://www.niftyadmin.cn/n/1037923.html

相关文章

【题目记录】——南京2019区域赛

文章目录A A Hard ProblemC Digital Path 记忆化搜索H Prince and PrincessK Triangle题目集地址 南京2019区域赛A A Hard Problem 题目地址 A A Hard Problem 题目大意&#xff1a;给出一个正整数n&#xff0c;找到一个最小的整数k使得集合1,2,…n{1,2,\dots n}1,2,…n的任意…

【题目记录】——ICPC沈阳2017

文章目录F Heron and His TriangleG Infinite Fraction PathI Little BoxesK RabbitsL TreeM Wandering Robots题目集地址 ICPC2017沈阳F Heron and His Triangle 题目地址F Heron and His Triangle 题目大意: 三条边为t-1,t,t1&#xff0c;同时满足面积为整数的三角形称为H三…

【题目记录】——ICPC青岛2018

文章目录C Flippy Sequence 组合数学 分类D Magic MultiplicationE Plants vs. ZombiesF TournamentJ BooksM Function and Function题目集地址 ICPC青岛2018提交题目地址 ZOJ4058-4070C D E F J M KC Flippy Sequence 组合数学 分类 题目地址C Flippy Sequence 题目大意&…

【题目记录】——ICPC南昌2019

文章目录B A Funny Bipartite Graph 状压DPE Bobs ProblemG Eating PlanK Tree 树上启发式合并L Who is the Champion 签到题目集地址 ICPC2019南昌B A Funny Bipartite Graph 状压DP 题目地址B A Funny Bipartite Graph 题目大意:一个二分图&#xff0c;左右各有n个点&#x…

【题目记录】——ICPC焦作2018

文章目录A Xu Xiake in Henan Province 签到B Ultraman vs. Aodzilla and Bodzilla 思维E Resistors in Parallel (思维数学)I Distance &#xff08;思维&#xff09;H&#xff08;后缀数组&#xff09;&#xff08;待补&#xff09;题目集地址 ICPC焦作2018A Xu Xiake in Hen…

【题目记录】——ICPC徐州2019

文章目录A Cat 规律C < 3 numbersF The Answer to the Ultimate Question of Life, The Universe, and Everything.L Loli, Yen-Jen, and a cool problem SAM(后缀自动机)M Kill The Tree 树的重心题目集地址 2019ICPC徐州站A Cat 规律 题目地址A Cat 题目大意:有101810^18…

【算法与数据结构】——字典树(Trie)

只记录用法和对应代码&#xff0c;对于过程不做过多解释。 字典树介绍 字典树又称Trie树&#xff0c;单词查找树&#xff0c;是一种树形结构&#xff0c;也是哈希树的一种变种&#xff0c;主要用于统计&#xff0c;排序和存储大量的字符串(但不限于字符串)。 利用字符串的公共…

【模板】——c与c++字符串常用方法

文章目录文件尾循环控制CCstring与char的转换string转char*char * 转stringstring转char[]char[]转stringC string与int的转换int转stringstring转intc与其它类型的数字转换C语言字符串与整数的转换char*转doubleint转char*char*转int文件尾循环控制 C 1.gets()函数&#xff…