2021.7.19~2021.7.25题解

本文介绍了多项算法竞赛中的问题解决策略,包括数论、排序、最短路径等算法的应用,并提供了具体代码实例。同时,针对不同题目,分别讲解了如何计算图形的三角形数量、ROC曲线的AUC计算、字符串匹配以及最短路问题的解决方案。
摘要由CSDN通过智能技术生成
/**
题目:
牛客2021-07-20 
UCF Local Programming Contest Round 1A 
E Sum of a Function 

题意:
定义f(n)为n的最小质因子,给定区间s,e,求区间内所有f(n)排序后最小的k个的和

思路:
e+100<=e<=s+1e6,大数可以利用1e6之内的素数来筛 
先通过欧拉筛筛选出1e6之内的素数
然后对大数进行区间筛
如果是第一次筛到就记录下它的最小质因子
如果没有被筛到就说明这个数就是质数,其最小质因子就是其本身 
最后将区间内的数的最小质因子进行排序,求前k个的和 
**/
#include<bits/stdc++.h>
#define int long long
typedef long long ll; 
using namespace std;

const int N=1e6+5;
int primes[N],cnt;     // primes[]存储所有素数
bool st[N];         // st[x]存储x是否被筛掉
int dashu[N];

void get_primes(int n) // 欧拉筛 复杂度O(n) 
{
    for (int i = 2; i <= n; i ++ )
    {
        if (!st[i]) primes[cnt ++ ] = i;
        for (int j = 0; primes[j] <= n / i; j ++ )
        {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

void qujianshai(int l,int r){ // 区间筛 
 	for(int i=0;i<=r-l;i++){// 下标偏移 
 		dashu[i]=i+l;
	} 
	for(int i=0;i<cnt;i++){
		int x=max(1LL*2,l/primes[i]);// l/primes[i]为[l,r]区间内的第一个数至少为i的多少倍.
//		ll x=l/primes[i];
//      if(x<=1) x=2;
		for(int j=x*primes[i];j<=r;j=j+primes[i]){ 
			if(j>=l){
				if(dashu[j-l]==j){// 当前这个数还是原来的数字,尚未求出其最小质因子 
					dashu[j-l]=primes[i];// 因数是primes[i] 
				}
			}
//			cout << j << endl;
			/**
			100 200 70
			j为: 100 102 104 106 ...200 99 102 105...100 105 110 ... 98 105 112... 
			**/ 
		}
	} 
}

signed main()
{
	// e+100<=e<=s+1e6,大数可以利用1e6之内的素数来筛 
	get_primes(N);// 先对1e6之内数字筛素数  
	int s,e,k;
	cin >> s >> e >> k;
	qujianshai(s,e);// 大数区间筛 
	sort(dashu,dashu+e-s+1);// 对这个区间内的的每个数的最小质因子进行排序,一共是e-s+1个 
	int res=0;
	for(int i=0;i<k;i++){// 求前k个之和 
		cout << dashu[i] << endl; 
		res=res+dashu[i];
	}
	cout << res << endl;
	return 0;
}
/**
题目:
牛客2021-7-23 
沈阳ICPC2021重现赛 
K Scholomance Academy

题意:
给出n组,每组包含一个字符(+/-)和一个数值
对于任意一个值x,如果x大于等于这个数值,并且为+,则属于TP,为-,则属于FP
反之如果x<这个值,并且为+,则属于FN,为-,则属于TN
现在定义TPR=#TP/(#TP+#FN),FPR=#FP(#TN+#FP)
TPR即为在所有+的数中属于TP的数量
DPR即为在所有-的数中属于FP的数量 
#表示取x时所属类别的数量
可以根据TPR和FPR画出曲线
要求根据的ROC曲线,求出AUC

思路:
横坐标为TPR
纵坐标为FPR
因为横纵坐标相乘后分母永远都是所有+的数量*所有-的数量
所以可以先算分子,最后再除以分母就可以了 
可以得出ROC是一个分段常数函数,这样就可以求AUC了 
可以根据FPR从小到大,依次判断比FPR大的数有几个 
将+和-的值分别放进两个数组里,进行从小到大的排序 
a.end()-upper_bound(a.begin(),a.end(),b[i]);返回大于b[i]的数字有几个 
**/

#include<bits/stdc++.h>
using namespace std;
vector<int>a;
vector<int>b;
int main()
{
	int n;
	cin >> n;
	for(int i=1;i<=n;i++){
		char c;
		int x;
		cin >> c >> x;
		if(c=='+'){
			a.push_back(x);
		}
		else{
			b.push_back(x);
		}
	}
	sort(a.begin(),a.end());
	sort(b.begin(),b.end());
	double ans=0;
	for(int i = 0; i < b.size();i++){
		ans=ans+a.end()-upper_bound(a.begin(),a.end(),b[i]);返回大于b[i]的数字有几个 
//		cout << ans << endl; 
	}
	printf("%.10lf\n",ans/(a.size()*b.size()));
	return 0;
}
/**
题目:
牛客2021-7-24
牛客多校训练营3 
J Counting Triangles

题意:
给出n个定点的无向图,图的每条边为黑色或白色
要求找出三角形(a,b,c)的数量(a<b<c),使得边有相同的颜色
题目已经给出了生成边的代码 

思路:
根据题目给出的生成边的代码,将其输出
可以发现是一个对称矩阵,有一个性质
每个三角形有两种情况:
1、都是同色
2、两边同色另一边异色
对于第二种情况,三角形恰好有两个异色角
因此异色角数/2即为不符合条件的三角个数,用总数减去即可 
**/

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
pair<int, int> v[8005];
ll ans, cnt;

namespace GenHelper
{
	unsigned z1, z2, z3, z4, b, u;
	unsigned get()
	{
		b = ((z1 << 6) ^ z1) >> 13;
		z1 = ((z1 & 4294967294U) << 18) ^ b;
		b = ((z2 << 2) ^ z2) >> 27;
		z2 = ((z2 & 4294967288U) << 2) ^ b;
		b = ((z3 << 13) ^ z3) >> 21;
		z3 = ((z3 & 4294967280U) << 7) ^ b;
		b = ((z4 << 3) ^ z4) >> 12;
		z4 = ((z4 & 4294967168U) << 13) ^ b;
		return (z1 ^ z2 ^ z3 ^ z4);
	}
	bool read() {
		while (!u) u = get();
		bool res = u & 1;
		u >>= 1; return res;
	}
	void srand(int x)
	{
		z1 = x;
		z2 = (~x) ^ 0x233333333U;
		z3 = x ^ 0x1234598766U;
		z4 = (~x) + 51;
		u = 0;
	}
}
using namespace GenHelper;
bool edge[8005][8005];
int main() {
	int n, seed;
	cin >> n >> seed;
	srand(seed); 
	for (int i = 0; i < n; i++){
		for (int j = i + 1; j < n; j++){
			edge[j][i] = edge[i][j] = read();
			if (edge[i][j]){
				v[i].first++;//黑 
				v[j].first++;//黑 
			}
			else{
				v[i].second++;//白 
				v[j].second++;//白 
			}
		}
	}
	for (int i = 0; i < n; i++){
		ans += (ll)v[i].first * (v[i].first - 1) / 2 + (ll)v[i].second * (v[i].second - 1) / 2;
		cnt += (ll)v[i].first * v[i].second;//异色角个数 
	}
	cout << (ans - cnt / 2) / 3 << endl;//(总数-不符合数)/3 
	return 0;
}
/**
题目:
actoder2021-7-24
ABC211
C chokudai

题意:
给一个字符串s,计算有多少种方法,选择8个字符使得其从左到右为"chokudai" 

思路:
f[j]表示到i的可以实现j个字母的方案数
**/

#include<bits/stdc++.h>
using namespace std;
string p = "chokudai";
const int M=1e9+7;
int f[8];
int main()
{
	string s;
	cin >> s;
	int len=s.length();
	for(int i=0;i<len;i++){
		for(int j=7;j>=0;j--){
			if(s[i]==p[j]){
				if(j==0) f[j]++;//为第一个字符c 
				else f[j]=(f[j]+f[j-1])%M;
			}
		}
	}
	cout << f[7] << endl;//实现7个字符chokudai的方案数 
	return 0;
}
/**
题目:
actoder2021-7-24
ABC211
D Number of Shortest paths

题意:
n个点,m条边
给出m条边的两个端点
求最短路的条数 

思路:
spfa
用ans数组记录1点到i点的最短路的数量 
当dist[j] == dist[t] + 1时,最短路增加ans[t]条 
**/

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n;      // 总点数
int h[N], e[N], ne[N], idx;       // 邻接表存储所有边
int dist[N];        // 存储每个点到1号点的最短距离
bool st[N];     // 存储每个点是否在队列中
const int M=1e9+7;
int ans[N];//用于记录路径数 

void add(int a, int b)  // 添加一条边a->b
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

// 求1号点到n号点的最短路距离
void spfa(){//spfa模板 
	memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    queue<int> q;
    q.push(1);
    st[1] = true;
	ans[1]=1;//初始为1 
    while (q.size())
    {
        int t = q.front();
        q.pop();

        st[t] = false;

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + 1)
            {
                dist[j] = dist[t] + 1;
                ans[j]=ans[t];//数量不变 
                if (!st[j])     // 如果队列中已存在j,则不需要将j重复插入
                {
                    q.push(j);
                    st[j] = true;
                }
            }
            else if(dist[j] == dist[t] + 1){// 
            	ans[j]=(ans[j]+ans[t])%M;//数量增加 
			}
        }
    }
}
int main()
{
	memset(h, -1, sizeof(h));
	int n,m;
	cin >> n >> m;
	for(int i=1;i<=m;i++){
		int x,y;
		cin >> x >> y;
		add(x,y);
		add(y,x);
	}
	spfa();
	cout << ans[n] << endl;//到n点的最短路数量 
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值