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