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;
}