文章目录
- Problem A: Appeal to the Audience
- Problem B: Breaking Branches
- Problem C: Conveyor Belts
- Problem D: Deck Randomisation
- Problem E: Efficient Exchange
- Problem F: Find my Family
- Problem G: Gluttonous Goop
- Problem H: Historic Exhibition
- Problem I: Inquiry II
- Problem J: Jazz it Up!
- Problem K: Keep Him Inside
- Problem L: Lucky Draw
题目和题解地址
Problem A: Appeal to the Audience
徐州热身赛的题目
长链剖分
建一个优先队列,对于每个非根节点将其值置为重儿子的值+1,所有轻儿子的值入队,然后将根节点的值-1加入队列与排序后的快乐值进行匹配
#include<bits/stdc++.h>
#define de(x) cout<<#x<<" = "<<x<<endl;
using namespace std;
typedef long long ll;
const ll MAXN=1e5+10;
ll cnt;
struct Edge{
ll to,next;
}edge[MAXN];
ll head[MAXN],dp[MAXN],val[MAXN],heavy[MAXN];
void add(ll u,ll v){
edge[cnt].to=v;
edge[cnt].next=head[u];
head[u]=cnt++;
}
priority_queue<ll>q;
void dfs(ll u){
dp[u]=1;
for(ll i=head[u];~i;i=edge[i].next){
ll v=edge[i].to;
dfs(v);
if(dp[v]+1>dp[u]){
dp[u]=dp[v]+1;
heavy[u]=v;
}
}
for(ll i=head[u];~i;i=edge[i].next){
ll v=edge[i].to;
if(v==heavy[u])continue;
q.push(dp[v]);
//de(dp[v]);
}
}
int main(){
memset(head,-1,sizeof(head));
ll n,k;
scanf("%lld%lld",&n,&k);
for(ll i=1;i<=k;i++){
scanf("%lld",&val[i]);
}
sort(val+1,val+k+1);
ll u;
for(ll v=1;v<n;v++){
scanf("%lld",&u);
add(u,v);
}
dfs(0);
q.push(dp[0]-1);
ll ans=0;
for(ll i=k;i>0;i--){
ll x=q.top();
//de(x);
q.pop();
//de(val[i]);
ans+=x*val[i];
}
printf("%lld\n",ans);
}
/*
5 3
5 4 3
0 0 1 1
*/
Problem B: Breaking Branches
签到题
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
ll n;
scanf("%lld",&n);
if(n&1){
printf("Bob\n");
}
else{
printf("Alice\n1\n");
}
}
Problem C: Conveyor Belts
构造题,个人认为这套题最难的题目,标程只有30行的代码orz
做法是先将a:b凑成1:1,在凑成c:d
利用类似二叉树的原理吧,好难写
c++高效位运算函数: _builtin
bitset的基本用法
#include<bits/stdc++.h>
#define de(x) cout<<#x<<" = "<<x<<endl;
using namespace std;
typedef long long ll;
ll remain[3],from[3],to[6];
ll a,b,c,d;
void connect(ll from,ll l,ll r){
printf("%lld %lld\n",from*3+1,from*3+2);
printf("%lld %lld\n",3*from,l>0?l*3:l);
printf("%lld %lld\n",r>0?r*3:r,3*from);
}
int main(){
scanf("%lld%lld",&a,&b);
scanf("%lld%lld",&c,&d);
ll cnt=1;
while(cnt<c+d){
cnt*=2;
}
remain[0]=d,remain[1]=c,remain[2]=cnt-d-c;
ll ans=3*(__builtin_popcountll(remain[0])+__builtin_popcountll(remain[1])+__builtin_popcountll(remain[2])-1);
printf("%lld\n",ans);
ll last=0,cur=1;
while(last<cur){
cnt>>=1;
ll out=0;
ll add=0;
for(ll i=0;i<3;i++){
if(remain[i]>=cnt){
remain[i]-=cnt;
to[out++]=i-2;
}
}
while(out<2*(cur-last)){
to[out++]=cur+(add++);
}
for(ll i=0;i<cur-last;i++){
connect(last+i,to[2*i],to[2*i+1]);
}
last=cur;
cur=cur+add;
}
return 0;
}
Problem D: Deck Randomisation
扩展中国剩余定理
对于所有的情况都满足第一种情况,但有可能有序列存在第二种也满足的情况。对于一个序列我们要判断是否对于它的所有循环节都存在满足第二种情况的情况,如果都存在最后flag为true,则两种情况都需要考虑,最后取最小值,否则flag为false,只需考虑第一种情况
import java.util.*;
import java.math.*;
public class Main {
static int MAXN=100010;
static int[] a=new int[MAXN];
static int[] b=new int[MAXN];
static int[] ab=new int[MAXN];
static int[] vis=new int[MAXN];
static BigInteger[] m=new BigInteger[MAXN];
static BigInteger[] r=new BigInteger[MAXN];
static int num;
static Vector<Vector<Integer>> xhj=new Vector<Vector<Integer>>();
static BigInteger d,x,y;
public static void Ex_gcd(BigInteger a,BigInteger b){
if(b.compareTo(BigInteger.ZERO)==0){
d=a;
x=BigInteger.ONE;y=BigInteger.ZERO;
}
else{
Ex_gcd(b,a.mod(b));
BigInteger tmp;
tmp=x;
x=y;
y=tmp.subtract(a.divide(b).multiply(y));
}
}
public static BigInteger Ex_Crt()
{
BigInteger a = m[1],rr=r[1];
for(int i=2;i<=num;++i)
{
BigInteger b = m[i];
BigInteger c = r[i].subtract(rr);
Ex_gcd(a,b);
c = c.divide(d);
b = b.divide(d);
x = x.multiply(c).mod(b).add(b).mod(b);
BigInteger lcm = a.multiply(b);
rr = x.multiply(a).mod(lcm).add(rr).mod(lcm);
a = lcm ;
}
return rr.equals(BigInteger.ZERO)? a:rr;
}
public static void main(String agvs[]) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
for(int i=1;i<=n;i++) {
a[i]=input.nextInt();
}
for(int i=1;i<=n;i++) {
b[i]=input.nextInt();
}
for(int i=1;i<=n;i++) {
ab[i]=a[b[i]];
}
int cnt=0;
BigInteger n1=BigInteger.ONE;
for(int i=1;i<=n;i++) {
if(vis[i]==0) {
xhj.add(new Vector<Integer>());
int cur=i;
int l=0;
while(vis[cur]==0) {
vis[cur]=1;
xhj.get(cnt).add(cur);
l++;
cur=ab[cur];
}
cnt++;
n1=n1.multiply(BigInteger.valueOf(l)).divide(n1.gcd(BigInteger.valueOf(l)));
}
}
boolean flag=true;
for(int i=0;i<xhj.size();i++) {
int sz=xhj.get(i).size();
for(int j=0;j<sz;j++) {
flag=true;
for(int k=0;k<sz;k++) {
if(a[xhj.get(i).get(k)]!=xhj.get(i).get((j+k)%sz)) {
flag=false;
break;
}
}
if(flag) {
m[++num]=BigInteger.valueOf(sz);
r[num]=BigInteger.valueOf((-j+sz)%sz);
break;
}
}
if(!flag)
break;
}
BigInteger n2,ans;
if(flag) {
n2=Ex_Crt().mod(n1).add(n1).mod(n1);
n1=n1.multiply(BigInteger.valueOf(2));
n2=n2.multiply(BigInteger.valueOf(2)).add(BigInteger.ONE);
ans=n1.min(n2);
}
else {
n1=n1.multiply(BigInteger.valueOf(2));
ans=n1;
}
if(ans.compareTo(new BigInteger("1000000000000"))>0) {
System.out.println("huge");
}
else {
System.out.println(ans);
}
input.close();
}
}
Problem E: Efficient Exchange
dp的思路:
给了一个数字n,我们从高位向低位递推
就比如样例给的83,我们可以先凑出80,也可以先凑出90
dp数组的第二维如果是0表示在当前这一位上我们使得当前的数字等于要凑的数,为1表示凑成要凑的数+1
比如83,dp[1][0]表示凑成80要多少次,dp[1][1]表示凑成90要多少次
然后dp[2][0]就是凑成83需要的次数,就是答案
递推式:
dp[i+1][0]=Math.min(dp[i][0]+s.charAt(i)-‘0’,dp[i][1]+10-s.charAt(i)+‘0’);
dp[i+1][1]=Math.min(dp[i][0]+s.charAt(i)-‘0’+1, dp[i][1]+10-s.charAt(i)+‘0’-1);
import java.math.*;
import java.util.*;
public class Main {
static String s;
static int MAXN=10000;
static int[][] dp=new int[MAXN][2];
public static void main(String agvs[]) {
dp[0][0]=0;
dp[0][1]=1;
Scanner input=new Scanner(System.in);
s=input.nextLine();
for(int i=0;i<s.length();i++) {
dp[i+1][0]=Math.min(dp[i][0]+s.charAt(i)-'0',dp[i][1]+10-s.charAt(i)+'0');
dp[i+1][1]=Math.min(dp[i][0]+s.charAt(i)-'0'+1, dp[i][1]+10-s.charAt(i)+'0'-1);
}
System.out.println(dp[s.length()][0]);
}
}
Problem F: Find my Family
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=3e5+10;
const ll INF=0x3f3f3f3f;
vector<ll>ans;
ll a[MAXN];
ll MAX[MAXN];
int main(){
ll k;
scanf("%lld",&k);
for(ll i=1;i<=k;i++){
ll n;
scanf("%lld",&n);
for(ll j=1;j<=n;j++)
scanf("%lld",&a[j]);
set<ll>se;
ll M=0;
for(ll j=n;j>0;j--){
if(a[j]>M){
MAX[j]=a[j];
M=a[j];
}
else{
MAX[j]=M;
}
}
for(ll j=1;j<=n;j++){
if(se.empty()||MAX[j]==a[j]){
se.insert(a[j]);
continue;
}
auto pos=se.upper_bound(a[j]);
if(pos!=se.end()){
if(*pos<MAX[j]){
ans.push_back(i);
break;
}
}
se.insert(a[j]);
}
}
ll sz=ans.size();
printf("%lld\n",sz);
for(ll i=0;i<sz;i++){
printf("%lld\n",ans[i]);
}
}
Problem G: Gluttonous Goop
小范围爆搜,大范围和小范围在最小的包括所有点的矩形中缺失的块数和小范围是一致的
import java.util.*;
import java.math.*;
public class Main {
static int MAXN=100;
static int INF=0x3f3f3f3f;
static boolean[][] vis=new boolean[MAXN][MAXN];
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int r=input.nextInt();
int c=input.nextInt();
int k=input.nextInt();
int num=k>20?20:k;
for(int i=1;i<=r;i++) {
String s=input.next();
for(int j=0;j<s.length();j++) {
if(s.charAt(j)=='#') {
vis[i+30][j+31]=true;
for(int t=-num;t<=num;t++) {
for(int y=-num;y<=num;y++) {
vis[i+30+t][j+31+y]=true;
}
}
}
}
}
int cnt=0;
int minr=INF,maxr=0,minc=INF,maxc=0;
for(int i=31-num;i<=30+r+num;i++) {
for(int j=31-num;j<=30+c+num;j++) {
if(vis[i][j]==true) {
cnt++;
minr=Math.min(minr, i);
maxr=Math.max(maxr, i);
minc=Math.min(minc, j);
maxc=Math.max(maxc, j);
}
}
}
if(cnt==0)
System.out.println(cnt);
else {
if(k<=20) {
System.out.println(cnt);
}
else {
int len=maxr-minr+1;
//System.out.println(len);
int wid=maxc-minc+1;
//System.out.println(wid);
int size=len*wid;
int lack=size-cnt;
//System.out.println(lack);
BigInteger ans=BigInteger.ONE;
ans=ans.multiply(BigInteger.valueOf(len+(k-num)*2));
ans=ans.multiply(BigInteger.valueOf(wid+(k-num)*2));
ans=ans.subtract(BigInteger.valueOf(lack));
System.out.println(ans);
}
}
}
}
Problem H: Historic Exhibition
贪心即可,网络流会超时
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e4+10;
int ans[MAXN];
struct node{
int x,id;
friend bool operator <(node a,node b){
return a.x==b.x?a.id<b.id:a.x<b.x;
}
}va[MAXN];
unordered_map<int,stack<int> >mp1,mp2;
int main(){
int p,v;
scanf("%d%d",&p,&v);
int a,b;
for(int i=1;i<=p;i++){
scanf("%d%d",&a,&b);
if(a==b){
mp1[a].push(i);
}
else{
mp2[min(a,b)].push(i);
}
}
for(int i=1;i<=v;i++){
scanf("%d",&a);
va[i].id=i;
va[i].x=a;
}
sort(va+1,va+1+v);
for(int i=1;i<=v;i++){
if(!mp1[va[i].x].empty()){
ans[va[i].id]=mp1[va[i].x].top();
mp1[va[i].x].pop();
}
else if(!mp2[va[i].x-1].empty()){
ans[va[i].id]=mp2[va[i].x-1].top();
mp2[va[i].x-1].pop();
}
else if(!mp2[va[i].x].empty()){
ans[va[i].id]=mp2[va[i].x].top();
mp2[va[i].x].pop();
}
else{
printf("impossible\n");
return 0;
}
}
for(int i=1;i<=v;i++){
printf("%d\n",ans[i]);
}
return 0;
}
Problem I: Inquiry II
dfs回溯+树形dp求最大独立集
#include<bits/stdc++.h>
#define de(x) cout<<#x<<" = "<<x<<endl;
using namespace std;
struct Edge{
int to;
int next;
}edge[200],edge2[200];
int cnt,cnt2,n,m;
int ans,sz,curans;
int head[200],dp[200][2],head2[200],visv[200],vise[200],unuse[200];
vector<int>outtree;
void add(int u,int v){
edge[cnt].to=v;
edge[cnt].next=head[u];
head[u]=cnt++;
}
void add2(int u,int v){
edge2[cnt2].to=v;
edge2[cnt2].next=head2[u];
head2[u]=cnt2++;
}
void build(int u,int fa){
visv[u]=1;
for(int i=head[u];~i;i=edge[i].next){
int v=edge[i].to;
if(v==fa)continue;
if(!visv[v]){
vise[i/2]=1;
add2(u,v);
add2(v,u);
build(v,u);
}
}
}
void treedp(int u,int fa){
if(!unuse[u])
dp[u][1]=1;
for(int i=head2[u];~i;i=edge2[i].next){
int v=edge2[i].to;
if(v==fa)continue;
treedp(v,u);
dp[u][0]+=max(dp[v][1],dp[v][0]);
dp[u][1]+=dp[v][0];
//de(dp[v][1]);
/*if(!unuse[u]){
if(!unuse[v]){
dp[u][0]+=max(dp[v][1],dp[v][0]);
dp[u][1]+=dp[v][0];
}
else{
dp[u][0]+=dp[v][0];
dp[u][1]+=dp[v][0];
}
}
else{
if(!unuse[v]){
dp[u][0]+=max(dp[v][1],dp[v][0]);
}
else{
dp[u][0]+=dp[v][0];
}
}*/
}
}
void dfs(int e){
if(e==sz){
memset(dp,0,sizeof(dp));
/*for(int i=1;i<=n;i++){
if(unuse[i])
cout<<i<<' ';
}
cout<<endl;*/
treedp(1,0);
/*if(unuse[1])
curans=dp[1][0];
else*/
curans=max(dp[1][0],dp[1][1]);
//de(curans);
//cout<<curans<<endl;
ans=max(ans,curans);
return ;
}
int u=edge[2*outtree[e]].to,v=edge[2*outtree[e]+1].to;
//de(u);
//de(v);
if(!unuse[u])
unuse[u]=e+1;
dfs(e+1);
if(unuse[u]==e+1)
unuse[u]=0;
if(!unuse[v])
unuse[v]=e+1;
dfs(e+1);
if(unuse[v]==e+1)
unuse[v]=0;
}
int main(){
memset(head,-1,sizeof(head));
memset(head2,-1,sizeof(head2));
int u,v;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
build(1,0);
for(int i=0;i<cnt/2;i++){
if(!vise[i])
outtree.push_back(i);
}
sz=outtree.size();
//de(sz);
dfs(0);
cout<<ans<<endl;
return 0;
}
/*
10 11
1 2
1 6
6 5
4 5
3 4
3 2
4 9
5 8
9 10
10 7
7 8
*/
Problem J: Jazz it Up!
签到题
数论
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=1e5+10;
ll prime[MAXN],cnt;
bool isprime[MAXN];
void init()
{
cnt=1;
memset(isprime,1,sizeof(isprime));
isprime[0]=isprime[1]=0;
for(ll i=2;i<=MAXN;i++)
{
if(isprime[i])
{
prime[cnt++]=i;
}
for(ll j=i*2;j<=MAXN;j+=i)
{
isprime[j]=0;
}
}
}
int main(){
init();
ll n;
ll ans;
scanf("%lld",&n);
for(ll i=1;i<=cnt;i++){
if(n%prime[i]){
ans=prime[i];
break;
}
}
printf("%lld\n",ans);
}
Problem K: Keep Him Inside
构造计算几何
给每一个点一个权重,使得这些点加权和等于p
这个点在多边形内部,找到这个点所在的三角形,然后解方程组
a
∗
x
1
+
b
∗
x
2
+
(
1
−
a
−
b
)
∗
x
3
=
p
x
,
a
∗
y
1
+
b
∗
y
2
+
(
1
−
a
−
b
)
∗
y
3
,
a
,
b
,
1
−
a
−
b
a*x_1+b*x_2+(1-a-b)*x_3=px,a*y_1+b*y_2+(1-a-b)*y_3,a,b,1-a-b
a∗x1+b∗x2+(1−a−b)∗x3=px,a∗y1+b∗y2+(1−a−b)∗y3,a,b,1−a−b是三角形三个点的答安,其他店的答案为0
#include<bits/stdc++.h>
using namespace std;
struct Point{
double x, y;
Point(double x = 0, double y = 0):x(x),y(y){}
}p,no[100];
double ans[100];
typedef Point Vector;
int Cross(Vector A, Vector B){
return A.x*B.y-A.y*B.x;
}
Vector operator - (Point A, Point B){
return Vector(A.x-B.x, A.y-B.y);
}
int main(){
int n;
scanf("%d%lf%lf",&n,&p.x,&p.y);
for(int q=1;q<=n;q++){
scanf("%lf%lf",&no[q].x,&no[q].y);
}
int i;
for(i=3;i<=n;i++){
if(Cross(no[i]-p,no[1]-no[i])>0)
break;
}
double x1=no[1].x,y1=no[1].y,x2=no[i-1].x,y2=no[i-1].y,x3=no[i].x,y3=no[i].y,px=p.x,py=p.y;
ans[1]=((x2-x3)*(y3-y2)-(px-x3)*(y3-y2)-(py-y2)*(x2-x3))/((x2-x3)*(y3-y2)-(x1-x3)*(y3-y2)-(y1-y2)*(x2-x3));
if(x2-x3==0){
ans[i]=(py-y2-(y1-y2)*ans[1])/(y3-y2);
ans[i-1]=1-ans[1]-ans[i];
}
else{
ans[i-1]=(px-x3-(x1-x3)*ans[1])/(x2-x3);
ans[i]=1-ans[i-1]-ans[1];
}
for(int k=1;k<=n;k++){
printf("%.8f\n",ans[k]);
}
}
Problem L: Lucky Draw
概率题
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1e3;
long double c[MAXN+10][MAXN+10];
void init(){
c[0][0]=1;
for(int i=1;i<=MAXN;i++){
c[i][0]=c[i][i]=1;
for(int j=1;j<i;j++)
{
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
}
}
int main(){
init();
int n,k;
long double p;
scanf("%d%d",&n,&k);
scanf("%llf",&p);
long double cur=0;
long double tot=0;
long double ans=1;
for(int i=1;i<=MAXN;i++){
if(i>=k){
long double P=c[i-1][k-1]*pow(p,i-k)*pow(1-p,k);
tot=tot+P*pow(cur,n-1);
cur=cur+P;
}
}
ans=1-n*tot;
printf("%.15Lf\n",ans);
return 0;
}