寫在前面:- -因爲D題的傻逼原因,導致完全不想做HIJ,後來發現H題和自己一眼秒的dp方程一樣,I題和J題稍微想想也能夠到。還是自己的鍋,再也不想碰計算幾何了(高度自閉)。
B-排數字
題意:給出一個長度爲n的字符串,問重新排列後最多能組成多少個616。
題解:只要計數6和1的個數就行了,甚至拿了個全場一血。
#include "stdio.h"
#include "string.h"
#include "algorithm"
#include "iostream"
#include "math.h"
#include "vector"
#include "map"
using namespace std;
const int maxn=2e5+10;
const double INF=4e9+10;
typedef long long ll;
int n,m,ans,sum;
char s[maxn];
int main(){
int T;
scanf("%d",&n);
scanf("%s",&s);
for(int i=0;i<n;i++)
if(s[i]=='6')ans++;
else if(s[i]=='1')sum++;
if(ans>sum)printf("%d\n",sum);
else printf("%d\n",ans-1);
return 0;
}
A-做遊戲
題意:兩個人玩石頭剪刀布,分別給出他們出石頭、剪刀、布的個數,問第一個人最多能贏多少次。
題解:不需要考慮順序,第一個人的石頭儘可能贏第二個人的剪刀,剪刀儘可能贏布,布儘可能贏石頭就好。注意範圍要開long long。
#include "stdio.h"
#include "string.h"
#include "algorithm"
#include "iostream"
#include "math.h"
#include "vector"
#include "map"
using namespace std;
const int maxn=2e5+10;
const double INF=4e9+10;
typedef long long ll;
ll a,b,c;
ll x,y,z;
int main(){
scanf("%lld%lld%lld",&a,&b,&c);
scanf("%lld%lld%lld",&x,&y,&z);
ll ans=min(a,y)+min(b,z)+min(c,x);
printf("%lld\n",ans);
return 0;
}
C-算概率
題意:有n道題,每道題各有的概率答對。問分別答對道題的概率爲多少。
題解:給出的是膜意義下的概率,實際上無需考慮轉換。可以發現一個顯然的dp方程。表示到第i題時答對j題的概率是,狀態轉移方程即爲。
#include "stdio.h"
#include "string.h"
#include "algorithm"
#include "iostream"
#include "math.h"
#include "vector"
#include "map"
using namespace std;
typedef long long ll;
const int maxn=2e3+10;
const ll mod=1e9+7;
const double eps=1e-8;
int n,p[maxn];
ll dp[maxn][maxn];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&p[i]);
dp[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=i;j++)
if(j==0)dp[i][j]=dp[i-1][j]*(1-p[i]+mod)%mod;
else dp[i][j]=(dp[i-1][j-1]*p[i]%mod+dp[i-1][j]*(1-p[i]+mod)%mod)%mod;
for(int i=0;i<n;i++)printf("%lld ",dp[n][i]);
printf("%lld\n",dp[n][n]);
return 0;
}
E-做計數
題意:簡單分析一下題意可以發現就是問小於等於n的所有完全平方數因子和。
題解:按照題意暴力即可。數據規模再大點可以考慮一下記憶化啥的。
#include "stdio.h"
#include "string.h"
#include "algorithm"
#include "iostream"
#include "math.h"
#include "vector"
#include "map"
using namespace std;
const int maxn=2e3+10;
const double INF=4e9+10;
typedef long long ll;
ll n,ans;
int main(){
scanf("%lld",&n);
for(int i=1;i*i<=n;i++){
for(int j=1;j*j<=i*i;j++)
if(i*i%j==0){
if(j*j==i*i)ans++;
else ans+=2;
}
}
printf("%lld\n",ans);
}
G-判正誤
題意:問是否正確。
題解:不知道爲什麼取模會是正確的做法。但是隨便寫了一個快速冪取模就過了。
#include "stdio.h"
#include "string.h"
#include "algorithm"
#include "iostream"
#include "math.h"
#include "vector"
#include "map"
using namespace std;
typedef long long ll;
const int maxn=2e3+10;
const ll mod=1e9+7;
const double eps=1e-8;
ll a,b,c,d,e,f,g;
int flag;
ll quick(ll a,ll b){
ll res=1;
while(b){
if(b&1)res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
flag=0;
scanf("%lld%lld%lld%lld%lld%lld%lld",&a,&b,&c,&d,&e,&f,&g);
a=quick(a,d);
b=quick(b,e);
c=quick(c,f);
if(a+b+c==g)puts("Yes");
else puts("No");
}
return 0;
}
F-拿物品
題意:有n堆物品,每個有兩個屬性a和b。兩個人分別拿,其中第一個人的價值爲a屬性之和,第二個人的價值爲b屬性之和,要求兩個人價值差要儘可能的大。
題解:很顯然的,按照每個物品的價值差排序。依次取即可。
#include "stdio.h"
#include "string.h"
#include "algorithm"
#include "iostream"
#include "math.h"
#include "vector"
#include "map"
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
const ll mod=1e9+7;
const double eps=1e-8;
int n,ans1[maxn],ans2[maxn];
struct arr{
int a,b,no;
}a[maxn];
bool cmp(arr a,arr b){
return a.a-b.b>b.a-a.b;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
a[i].a=x;a[i].no=i;
}
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
a[i].b=x;
}
sort(a+1,a+1+n,cmp);
int cnt=0,num=0,sum=0;
while(sum<n){
sum++;
if(sum%2)ans1[cnt++]=a[sum].no;
else ans2[num++]=a[sum].no;
}
for(int i=0;i<cnt-1;i++)printf("%d ",ans1[i]);printf("%d\n",ans1[cnt-1]);
for(int i=0;i<num-1;i++)printf("%d ",ans2[i]);printf("%d\n",ans2[num-1]);
return 0;
}
D-數三角
題意:給出n個不重合的點,問最多能組成多少個鈍角三角形。
題解:你跟我說能過?現場現學極角排序的應用,結果發現完全沒必要?暴力三個點,check能否組成三角形即可。想學極角排序的可以點擊。
#include "stdio.h"
#include "string.h"
#include "algorithm"
#include "iostream"
#include "math.h"
#include "vector"
#include "map"
using namespace std;
const int maxn=2e3+10;
const double INF=4e9+10;
typedef long long ll;
int n,ans;
struct arr{
int x,y;
}f[maxn];
double a[2020][2020];
inline int read(){
int f=1,x=0;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
return f*x;
}
inline bool check(int i,int j,int k){
if((((f[j].x-f[i].x)*(f[k].x-f[i].x)+(f[j].y-f[i].y)*(f[k].y-f[i].y))<0) &&
(((f[j].x-f[i].x)*(f[k].y-f[i].y)-(f[j].y-f[i].y)*(f[k].x-f[i].x))!=0))return true;
return false;
}
int main(){
n=read();
for(int i=1;i<=n;i++){
int x,y;
x=read();y=read();
f[i].x=x,f[i].y=y;
}
for(int i=1;i<n-1;i++)
for(int j=i+1;j<n;j++)
for(int k=j+1;k<=n;k++)
if(check(i,j,k) || check(k,i,j) || check( j,i,k))ans++;
printf("%d\n",ans);
return 0;
}
上面的題目就是比賽的時候出的了,後面一個多小時溜出去玩英雄聯盟了,不然估計還能出1~2題。
下面是補了的題目,難度不分先後。
J-求函數
題意:給出n個形如的函數,並且給出m個操作。第一種操作,將函數的改變。第二種,詢問。
題解:一眼可以看出來是個線段樹的題目,那麼線段樹上面維護什麼呢。首先對於k和b肯定要分別建樹。我們觀察可以發現對於答案的k,他是累乘形式的就相當於。對於答案的b他是符合形如。然後正常建樹單點修改區間查詢即可。
#include "stdio.h"
#include "string.h"
#include "algorithm"
#include "iostream"
#include "math.h"
#include "vector"
#include "map"
using namespace std;
const int maxn=2e5+10;
const double INF=4e9+10;
const int mod=1e9+7;
typedef long long ll;
int n,m,k[maxn],b[maxn];
struct arr{
ll k,b;
}s[maxn*4];
void pushup(int node){
s[node].k=s[node<<1].k*s[node<<1|1].k%mod;
s[node].b=(s[node<<1|1].k*s[node<<1].b%mod+s[node<<1|1].b)%mod;
}
void build(int node,int l,int r){
int mid=(l+r)>>1;
if(l==r){
s[node].k=1ll*k[l];
s[node].b=1ll*b[l];
return ;
}
build(node<<1,l,mid);
build(node<<1|1,mid+1,r);
pushup(node);
}
void modify(int L,int k,int b,int l,int r,int node){
if(l==r){
s[node].k=1ll*k;
s[node].b=1ll*b;
return;
}
int mid=(l+r)>>1;
if(L<=mid)modify(L, k, b, l, mid, node<<1);
else modify(L, k, b, mid+1, r, node<<1|1);
pushup(node);
}
arr push(arr a,arr b){
arr newo;
newo.k=(a.k*b.k)%mod;
newo.b=(b.k*a.b+b.b)%mod;
return newo;
}
arr query(int L,int R,int l,int r,int node){
if(L<=l && r<=R)return s[node];
int mid=(l+r)>>1;
arr sum={1,0};
if(L<=mid)sum=push(sum,query(L, R, l, mid, node<<1));
if(R>mid)sum=push(sum,query(L, R, mid+1, r, node<<1|1));
return sum;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&k[i]);
for(int i=1;i<=n;i++)scanf("%d",&b[i]);
build(1,1,n);
while(m--){
int opt;
scanf("%d",&opt);
if(opt==1){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
modify(x,y,z,1,n,1);
}else{
int l,r;
scanf("%d%d",&l,&r);
arr ans=query(l,r,1,n,1);
printf("%lld\n",(ans.k+ans.b)%mod);
}
}
return 0;
}
H-施魔法
題意:有n個元素,每個有一個價值ai。你每次至少選擇k個,代價爲你選擇的k個裏的最大值減最小值。問恰好全部選擇所有元素的總代價。
題解:表面dp。將ai從小到大排序,最佳代價很顯然,可以用一個前綴和維護,而ai是個遞增的固定代價,每一次都累加即可。
#include "stdio.h"
#include "string.h"
#include "algorithm"
#include "iostream"
#include "math.h"
#include "vector"
#include "map"
using namespace std;
typedef long long ll;
const int maxn=3e5+10;
const ll INF=1e18;
const int mod=1e9+7;
int n,k;
int a[maxn];
ll dp[maxn],f[maxn];
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
sort(a+1,a+1+n);
f[0]=INF;
for(int i=1;i<=n;i++){
f[i]=min(dp[i-1]-1ll*a[i],f[i-1]);
if(i-k+1>=1)dp[i]=f[i-k+1]+1ll*a[i];
else dp[i]=INF;
}
printf("%lld\n",dp[n]);
return 0;
}
I-建通道
題意:n個點,每個點有一個權值ai。連接兩個點的代價爲。求連接這n個點的最小花費。
題解:首先去重,假設去重後有n個數。數值大小一樣的點花費肯定爲0。之後找出一個最小的二進制位k,滿足存在的這個二進制位是0且存在的這個二進制位1,答案就是。(這位是0的點與連邊,是1的點與連邊)。
#include "stdio.h"
#include "string.h"
#include "algorithm"
#include "iostream"
#include "math.h"
#include "vector"
#include "map"
using namespace std;
typedef long long ll;
const int maxn=3e5+10;
const ll INF=1e18;
const int mod=1e9+7;
int n,a[maxn];
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d",&a[i]);
sort(a,a+n);
ll ans=0;
n=unique(a, a+n)-a;
for(int i=0;i<=30;i++){
int cnt=0;
for(int j=0;j<n;j++)if((a[j]>>i)&1)cnt++;
//a[j]>>i &1就是看二進制位第i位是否爲1
if(cnt>0 && cnt<n){
ans=(1ll<<i)*(n-1);
break;
}
}
printf("%lld\n",ans);
return 0;
}