训练赛

A - Engines
题目链接:https://vjudge.net/contest/343420#status/Zreo917/A
思路:每个点都可以看作是一个起点是原点的向量,两向量相加,夹角越小时合向量的模越大(夹角小于90º)所以为了找到那个最远的点,需要让每个向量和与他夹角最小的向量相加,所以就要用到极角排序,对每一点暴力遍历,同时更新最大值

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1000;
struct node
{
	int x,y;
	double d;
}e[maxn];
bool cmp(node a,node b)
{
	return a.d<b.d;
}
int main()
{
	int n;
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>e[i].x>>e[i].y;
		e[i].d=atan2(e[i].y,e[i].x);
	}
	sort(e,e+n,cmp);
	ll ans=0;
	for(int i=0;i<n;i++)
	{
		ll dx=e[i].x,dy=e[i].y;
		ans=max(ans,(ll)dx*dx+dy*dy);
		for(int j=(i+1)%n;j!=i;j=(j+1)%n)//防止数组越界两次都要取余
		{
			dx=dx+e[j].x;dy=dy+e[j].y;
			ans=max(ans,(ll)dx*dx+dy*dy);
		}
	}
	printf("%.15lf",sqrt(ans)); 
	return 0;
 } 

B - Consecutive Integers
题意:
给你n个数(1~n) 求选k个连续数的方案 n-k-1

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
	int n,k;
	cin>>n>>k;
	cout<<n-k+1;
	return 0;
}

C - ModSum
题目链接:https://vjudge.net/contest/343420#problem/C
题意:
有n个数的一个集合(1~n),求1到n这那个数对集合内的数取余后的和的最大值
为是余数最大 那么全都对n取余 则和为: k*(k-1)/2

在这里插入代码片#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
	ll n,k;
	cin>>n;
	ll res=n*(n-1)/2;
	cout<<res;
	return 0;
}

D - Shortest Path on a Line
链接:https://vjudge.net/contest/343420#problem/D
题意: 有n的点在一条线上(标号1~n) 每给出 l,r,w 就在 l 和 r之间任意两点之间加权值为 w的无向边 一根一根加肯定会超时的 怎么办呢 在l和r 之间加一条权值是我都变 他们相邻中间点加权为0的反向边 (即:点i->点i-1 权值为0) 这样是不是对任意的L ≤ s < t ≤ R,都有路径为 s->…-> L -> R -> …->t的路实现 s->t 而且 权值刚好是w (太妙了!!!) 之后就好办了 跑一遍最短路就行了

#include <bits/stdc++.h>
#define ll long long
#define pll pair<ll, ll>
#define pii pair<int, int>
#define f first
#define se second
#define pb push_back
#define ld long double
 
using namespace std;
 
 
const int N = 2e5 + 123;
const int MAXN = 1e5 + 12;
const int inf = 1e9 + 7;
const ll mod = 1e9 + 7;
 

ll n, m, d[N];
bool vis[N], is[N];
vector <pll> g[N];
 
 
void djk() {
    priority_queue <pair<ll, ll> > q;
    q.push({0, 1});
    pair<ll, ll> tmp;
    ll u, w;
    while (!q.empty()) {
        tmp = q.top();
        q.pop();
        u = tmp.se;
        w = -tmp.f;
        if (vis[u])
            continue;
        vis[u] = 1;
        d[u] = w;
        if (u != 1 && !vis[u - 1])
            q.push({-w, u - 1});
        for (int i = 0; i < g[u].size(); i++)
            if (!vis[g[u][i].f])
                q.push({-w - g[u][i].se, g[u][i].f});
    }
} 
 
 
int main() {
    cin >> n >> m;
    ll x, y, z;
    for (int i = 1; i <= m; i++) {
        cin >> x >> y >> z;
        g[x].pb({y, z});
    }
    memset(d, -1, sizeof(d));
    djk();
    cout << d[n];
    return 0;
}

E - Counting of Trees
题目链接:https://vjudge.net/contest/343420#problem/E
树的节点编号1到n;给你一个数组d[n],d[i]表示节点1到节点 i 的边的数量,求满足条件的树的个数
假设 到1点距离为i(i>1)的点有 num[i] 个,那么这几个节点都可以与距离为i-1的点 相连,每个点有num[i-1]种情况,这一距离就要pow(num[i-1] , num[i]) 种情况了,遍历一遍就可以了 除此之外还有几种特殊情况需要特判

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
ll ksm(ll a,ll b)
{
	ll ans=1;
	while(b)
	{
		if(b&1) ans=ans*a%mod;
		b/=2;
		a=a*a%mod;
	}
	return ans;
}
int main()
{
	ll n,x,ans=1,s[100100],num[100100];
	memset(num,0,sizeof num);
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>x;
		if(!i&&x) ans=0;//d[1]>0
		else if(i&&!x) ans=0;//d[i]==0
		num[x]++;
	}
	if(ans)
	{
//		ans=num[1];
		for(int i=2;i<n;i++)
			if(num[i]&&num[i-1])
				ans=ans*ksm(num[i-1],num[i])%mod;
			else if(num[i]&&!num[i-1]) ans=0;//跳过了一些点 是不可能的
	}
	cout<<ans;
	return 0;
}

F - Monsters Battle Royale
题目链接:https://vjudge.net/contest/343420#problem/F
题意:n个人有不同的健康值,健康值地的可以攻击健康值高的人,被攻击的人的健康值减少量是攻击人的健康值求最后的 最小值.
思路:
求两个人最后的健康值不就是辗转相除法,即求最大公因数

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
int main()
{
	ll n,s[maxn];
	scanf("%lld",&n);
	for(int i=0;i<n;i++)
		scanf("%lld",&s[i]);
	ll ans=s[0];
	for(int i=1;i<n;i++)
		ans=__gcd(ans,s[i]);
	cout<<ans;
}

G - Powerful Discount Tickets
题目链接:https://vjudge.net/contest/343420#problem/G
题意:
共有n件商品,你有m张券,需要把所有商品买完,有y张券买价值为x的商品你需要付x/pow(2,y) 元(向下取整),问需要最少付多少元
思路:
x/pow(2,y) 就是说每用一张券,需要付的钱就是当前商品价值的一半(整除)
为了实现最小,就要尽可能多的去除价值高的商品,就可以一张一张的去除,每张券都要去除当前价值最高的那个,就要用优先队列了

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

int main()
{
	priority_queue<ll,vector<ll>,less<ll> > q;//从大到小排,价值高的优先级高 从小到大排是 priority_queue<ll,vector<ll>,greater<ll> > q;
	int n,m;
	ll x;
	cin>>n>>m;
	for(int i=0;i<n;i++)
	{
		cin>>x;
		q.push(x);
	}
	while(m)
	{
		ll t=q.top()/2;
		q.pop();
		q.push(t);
		m--;
	}
	ll sum=0;
	while(!q.empty())
	{
		sum+=q.top();
		q.pop();
	}
	cout<<sum;
	return 0;
}

H - Attack Survival
题目链接:https://vjudge.net/contest/343420#problem/H
题意:n个人初始有k分,共回答q个问题,除了回答正确的人其他人全部减一分 胜者分数不变,经过k个问题后分数大于0的人胜 并输出每个人是否胜
胜者不变,输者减一 是不是相当于胜者加一,不过最后不是判断d[i]是否大于0了而是是否大于k-q

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
int main()
{
	int c[maxn],num[maxn],n,q,k,x;
	memset(num,0,sizeof num);
	scanf("%d%d%d",&n,&k,&q);
	for(int i=1;i<=q;i++)
	{
		scanf("%d",&x);
		num[x]++;
	}
	for(int i=1;i<=n;i++)
	{
		if(num[i]<=q-k) puts("No");
		else puts("Yes");
	}
	return 0;
}

J - Kleene Inversion
题目链接:https://vjudge.net/contest/343420#problem/J
题意: 有一个数字串 将这个串循环k次 求任意 i>j 且s[i]<s[j]的情况数
思路: 预处理给定字符串,求h[i]和sum[i] (h[i]:第i个数后有几个数比它小; sum[i]: 全串有几个数比它小) 对于整个循环串 ,第一个子串的第i个数后边比他小的数有 h[i]+sum[i](k-1) 而第二子串有 h[i]+sum[i](k-2) 以此类退 第k个子串有h[i]
所以: 所有子串的第i个数有 h[i]k+sum[i](k*(k-1)/2) ,遍历一遍相加即可(记得取模)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int main()
{
	int n,k,s[5000];
	cin>>n>>k;
	for(int i=0;i<n;i++)
	{
		scanf("%d",&s[i]);
	}
	int h[5000],sum[5000];
	memset(h,0,sizeof h);
	memset(sum,0,sizeof sum);
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<i;j++)
			if(s[i]>s[j])
				sum[i]++;
		for(int j=i+1;j<n;j++)
			if(s[i]>s[j])
				h[i]++;
		sum[i]+=h[i];
	}
	ll ans=0;
	//cout<<(ll)k*(k-1)/2%mod<<endl; 
	for(int i=0;i<n;i++)
	{
		ans=(ans%mod+(ll)h[i]*k%mod)%mod;
		ans=(ans%mod+((((ll)k*(k-1)/2)%mod)%mod*sum[i])%mod)%mod;//每个可能爆 ll 的情况都要mod掉(k*k还会爆 int)
	}
	cout<<ans%mod;
	return 0;
}```
K - Two Contests 
链接:https://vjudge.net/contest/343420#problem/K
思路:将所有区间分为两个集合,求两个集合公共区间和的最大值;
先求出来	最大区间左端点lmax(区间记为q) 最小区间右端点rmin(区间记为p)
那么会有两种情况 qp在同一集合,pq不在一个集合
pq在同一集合,那么这个集合的公共区间就确定了,不管其他区间是否在这个集合 公共区间的长度就是max(rmin-lmax+1,0) (确保值非负)	那么把其他区间最长的那个单独放到另一集合所得到答案是最大
如果pq不在同一集合, ,先把所有区间按l从小到大排序,开lx[i]表示前i个集合的最大左端点  rx[i]表示 前 i个区间的最小右端点	ly[i] 表示第i个区间 后边区间的最大右端点,ry[i] 表示	表示第i个区间 后边区间的最小左端点 ,数组更新之后就该求最大公共区间了,遍历n个区间,到第i个时	max(0,rx[i]-lx[i]+1)表示前i个区间的最大公共区间,max(0,ry[i+1]-ly[i+1]+1) 表示其他区间的最大公共区间,按l排序后应该把所有最大可能的情况遍历完了,求得这种情况的最大值,最后比较两种情况的最大值

```cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
struct node
{
	int l,r;
}e[maxn];
bool cmp(node a,node b)
{
	return a.l<b.l;
}

int main()
{
	int n,p,q,lmax=0,rmin=0x3f3f3f3f;
	cin>>n;
	for(int i=0;i<n;i++)
	{
		scanf("%d%d",&e[i].l,&e[i].r);
		if(lmax<e[i].l) lmax=e[i].l,p=i;
		if(rmin>e[i].r) rmin=e[i].r,q=i;
	}
		
	sort(e,e+n,cmp);
	int lx[maxn],rx[maxn];
	lx[0]=e[0].l,rx[0]=e[0].r;
	for(int i=1;i<n;i++)
	{
		lx[i]=max(lx[i-1],e[i].l);
		rx[i]=min(rx[i-1],e[i].r);
	}
	int ly[maxn],ry[maxn];
	ly[n-1]=e[n-1].l,ry[n-1]=e[n-1].r;
	for(int i=n-2;i>=0;i--)
	{
		ly[i]=max(ly[i+1],e[i].l);
		ry[i]=min(ry[i+1],e[i].r);
	int ans1=0,ans2=0;
	for(int i=0;i<n;i++)
		if(i!=q&&i!=p)
		ans1=max(ans1,max(0,rmin-lmax+1)+e[i].r-e[i].l+1);
	for(int i=0;i<n-1;i++)
	{
		ans2=max(ans2,max(0,rx[i]-lx[i]+1)+max(ry[i+1]-ly[i+1]+1,0));
	}
//	cout<<ans1<<' '<<ans2<<endl;
	cout<<max(ans1,ans2)<<endl;
	return 0;
 } 

M - AB Substrings
链接:https://vjudge.net/contest/343420#problem/M
题意: 有n个字符串,问拼接后出现字符"AB"的个数,
遍历每个字符串, 串首是B而且串尾是A时 num1++ ; 只有串首是B时 num2++; 只有串尾是A时 num3++; 字符串中间出现AB时 ans++,最后再比较 num1,num2,num3可以拼出的最多的AB

#include <bits/stdc++.h>
typedef  long long ll;

 
using namespace std;
 
 
const int N = 2e5 + 123;
const int MAXN = 1e5 + 12;
const int inf = 1e9 + 7;
const ll mod = 1e9 + 7;
ll n, m, d[N];
bool vis[N];
vector <pair<ll,ll> > g[N];
void djk() {
    priority_queue <pair<ll, ll> > q;
    q.push({0, 1});
    pair<ll, ll> tmp;
    ll u, w;
    while (!q.empty()) {
        tmp = q.top();
        q.pop();
        u = tmp.second;
        w = -tmp.first;
        if (vis[u])
            continue;
        vis[u] = 1;
        d[u] = w;
        if (u != 1 && !vis[u - 1])
            q.push({-w, u - 1});
        for (int i = 0; i < g[u].size(); i++)
            if (!vis[g[u][i].first])
                q.push({-w - g[u][i].second, g[u][i].first});
    }
} 
 
int main() {
    cin >> n >> m;
    ll x, y, z;
    for (int i = 1; i <= m; i++) {
        cin >> x >> y >> z;
        g[x].push_back({y, z});
    }
    memset(d, -1, sizeof(d));
    djk();
    cout << d[n];
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章