问题 A: 纽约
题目描述
印度洋暖流温润着纽约,四季丰沛的雨水造就了一望无际的大草原。蒙古包是纽约最独特的一道风景线,每至二月中旬,纽约的土著傣族人民又开始半年一度的转场了。
由于牲畜和行李过多,牧民Azone 不得不多次往返于两个草场之间运输家当。为了顺利转场,Azone 决定花费w 元津巴布韦币,购买一辆载重为w 的汽车。共有n 件家具需要搬运,每件家具的重量为wi。Azone 每次出发前,会搬若干件总重不超过w 的物品上车:出发前,车是空载的,Azone 会选择能搬上车的家具中最重的一件放上车(即该家具之前还未运走且放置该家具后汽车不会超载),然后在剩下的家具中继续选择一件能被搬走的最重的上车,持续装车,直至剩下的家具都塞不上车。装载完毕后,Azone 会开车运走这些家具,卸在目的地,再驾空车返回继续运送,直至转场完毕。
Azone 希望在运送次数不超过R 的情况下完成转场,求Azone 最少需要购置价值多少的车。
输入
第一行,两个整数n 和R,分别表示家具件数及最多运送次数。
第二行,若干个整数wi,表示家具重量。
输出
一行,表示答案。
样例输入
6 2
26 7 10 30 5 4
样例输出
42
提示
对于100% 的数据,1<=R, n,wi<=2000。
题解:
这是一条特殊的二分,有些复杂。当打完二分以后,一对拍,就会发现,其实它并不满足二分性。
虽然说二分出来的这个答案一定是满足题意的,但它不一定是最小的。通过比较正确答案与二分出来的答案,就会发现这两个答案之间相差不会太大,于是二分出来答案以后,再往前那么几百,判断一下前面是否有更优的答案。
// A.二分
#include <bits/stdc++.h>
#define G getchar
using namespace std;
char ch;
void read(int& n) {
for(n=0,ch=G();ch<'0' || ch>'9';ch=G());
for(;'0'<=ch && ch<='9';ch=G())n=(n<<3)+(n<<1)+ch-48;
}
int n,R,a[2003],l,r,mid,ans,pre[2003],nxt[2003];
bool pd(int w)
{
for(int i=1;i<=n;i++)
pre[i]=i-1,nxt[i]=i+1;pre[n+1]=n;
for(int m=1,s=0,x=n;s<n;x=pre[n+1],m++) {
if(m>R) return 0;
for(int p=w;p>0 && x;x=pre[x])
if(p>=a[x]) {
s++;p-=a[x];
nxt[pre[x]]=nxt[x];
pre[nxt[x]]=pre[x];
}
}
return 1;
}
int main()
{
read(n);
read(R);
for(int i=1;i<=n;i++) read(a[i]),r+=a[i],l=max(l,a[i]);
sort(a+1,a+1+n);
while(l<r) {
mid=(l+r)>>1;
if(pd(mid))r=mid;else l=mid+1;
}
for(ans=l-50;ans<=l && !pd(ans);ans++);
printf("%d",ans);
return 0;
}
问题 C: 山景城
题目描述
山景城毗邻雷克雅未克,位于美国东南部,高楼林立,彻夜灯明,是美国乃至世界的经济贸易中心。最有名的地标建筑当属南岸花园广场,这里不仅是洛杉矶银河队的主场,并且众多国际歌星,如二人转演员蕾哈娜,伟大的人民艺术家pdd 都曾在这里举办过演唱会。
花园广场球馆的内部由n 个独立的展厅组成,不同展厅间由n-1 条双向的走廊连接而互相可达。二月三十一号那天,花园广场球馆将会对公众开放展览文艺复兴时期的代表作品《蒙娜丽莎的假笑》,怪盗基德闻声披着夜色降临于此。不过Kris 透过馆内高端的红外线监控设施,很快便发现了基德,怪盗基德降临在s 号展厅,而Kris 此刻所在的监控室位于展厅t,Kris 想引诱基德至展厅t 以瓮中捉鳖。
从基德进入南岸花园广场球馆后的那一刻开始,双方便开始了斗智斗勇,在第i 秒钟:
? Kris 在这一秒的开始,可以进行决策:1) 不操作;2) 炸毁一条还存在的走廊,操作过后这条走廊将不复存在;3) 清除一条走廊上被怪盗基德留下标记。
? 怪盗基德在这一秒的末尾,有可能会移动:假设基德此刻正处于p 号展厅,若从p 号展厅出发通往相邻展厅的每条走廊都被画上了标记,那么基德会原地不动;否则,基德会从p 出发的未被标记的走廊中选择一个,走到对应的展厅,并在这条走廊上留下标记(若存在从p 出发的未被标记的走廊,基德必须移动,不能选择原地停留)。(注意:以上过程中Kris 先决策,决策完了之后基德再会行动)
在这一秒的博弈结束后,若怪盗进入了t 号展厅,博弈停止,否则进入下一秒的博弈。
Kris 炸毁走廊与清除标记都算作一次操作。Kris 希望用尽量少的操作次数抓住基德;而基德希望Kris 用尽量多的操作才能抓住他,并在Kris 眼皮底下逃之夭夭,以羞辱他。
由于怪盗基德过于神仙,而Kris 具有上帝视角,双方都十分清楚对方的心思,知道双方的位置,并熟知花园广场球馆的地图。此次是怪盗基德第一次造访花园广场球馆,所以所有的走廊在博弈开始前都
没有任何标记。双方都会采取最优决策,请求出Kris 操作的次数。
输入
第一行,三个整数n,t, s。
接下来n-1 行,每行两个整数ui, vi,描述一条连接ui 与vi 的通道。
输出
一行,表示答案。
样例输入
10 1 4
1 2
2 3
2 4
3 9
3 5
4 7
4 6
6 8
7 10
样例输出
4
提示
对于100% 的数据,1<= t, s, n<=10^6。
// C.模拟、矩形覆盖问题
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(x,y) (x<y?x:y)
#define max(x,y) (x>y?x:y)
using namespace std;
int a[2000001][2];
int ls[1000001],f[1000001];
int d[1000001],num[1000001];
int i,j,k,n,S,T,len,m,Find,N,l,r,mid;
bool Bz[1000001];
int D[1000001][2];
bool bz;
int p[1000001][6];
bool b[1000001];
int Len;
void New(int x,int y)
{
len++;
a[len][0]=y;
a[len][1]=ls[x];
ls[x]=len;
num[x]++;
}
void init()
{
memset(b,0,sizeof(b));
Len=1;
p[1][0]=T;
p[1][1]=0;
p[1][2]=0;
p[1][3]=0;
p[1][4]=0;
p[1][5]=ls[T];
while (Len)
{
for (; p[Len][5] && a[p[Len][5]][0]==p[Len][1]; p[Len][5]=a[p[Len][5]][1]);
if (p[Len][5] && !b[a[p[Len][5]][0]])
{
Len++;
p[Len][0]=a[p[Len-1][5]][0];
p[Len][1]=p[Len-1][0];
p[Len][2]=p[Len-1][2]+num[a[p[Len-1][5]][0]]-1-Bz[a[p[Len-1][5]][0]]+(a[p[Len-1][5]][0]==S);
p[Len][3]=0;
p[Len][4]=0;
p[Len][5]=ls[a[p[Len-1][5]][0]];
}
for (; Len && !p[Len][5]; Len--)
{
f[p[Len][0]]=(!p[Len][4])?p[Len][2]:f[p[Len][4]];
b[p[Len][0]]=1;
}
if (!Len) return;
if (b[a[p[Len][5]][0]])
{
if (f[a[p[Len][5]][0]]>f[p[Len][3]])
p[Len][4]=p[Len][3],p[Len][3]=a[p[Len][5]][0];
else
if (f[a[p[Len][5]][0]]>f[p[Len][4]])
p[Len][4]=a[p[Len][5]][0];
p[Len][5]=a[p[Len][5]][1];
}
}
}
void Fd()
{
int i,h,t;
h=0,t=1;
D[1][0]=T;
D[1][1]=0;
b[T]=1;
while (h<t)
{
for (i=ls[D[++h][0]]; i && !b[a[i][0]]; i=a[i][1])
{
D[++t][0]=a[i][0];
D[t][1]=h;
b[a[i][0]]=1;
if (D[t][0]==S) break;
}
if (D[t][0]==S) break;
}
for (; t; d[++m]=D[t][0],Bz[d[m]]=1,t=D[t][1]);
}
bool pd(int t)
{
int i,j,cut,lastcut;
cut=0;
fo(i,1,m-1)
{
lastcut=cut;
for (j=ls[d[i]]; j; j=a[j][1])
if (!Bz[a[j][0]]) {
if (lastcut+f[a[j][0]]>t)
cut++;
}
if (cut>i || cut>t)
return 0;
}
return 1;
}
int main()
{
scanf("%d%d%d",&n,&T,&S);
fo(i,2,n) {
scanf("%d%d",&j,&k);
New(j,k),New(k,j);
}
Fd();
init();
l=0,r=n*2;
while (l<r)
{
mid=(l+r)/2;
if (!pd(mid)) l=mid+1;
else r=mid;
}
printf("%d\n",l);
return 0;
}
问题 D: 话痨树
题目描述
在虚拟世界里有一种计算工具,叫做话痨树。
这种工具可以计算出小X所在国家任意两个城市的距离。
但话痨树太吵了,小X非常讨厌它,所以请穿越到异世界的你写一个工具实现话痨树的功能。这样小X就不用听话痨树唠叨了。
输入
第一行两个整数n和q,代表小X国家有n个城市,他想进行q次询问。
接下来n-1行每行三个整数u,v,k,代表城市u与城市v之间有一条双向道路,道路长度为k。
接下来q行,每行两个整数u,v,代表小X想知道u与v城市之间的距离。
输出
q行,每行是u与v城市之间的距离
样例输入
3 2
1 2 10
3 1 15
1 2
2 3
样例输出
10
25
提示
对于30%的数据,2<=n<=2000,1<=m<=2000
对于全部数据,2<=n<=10000,1<=q<=20000,1<=k<=500,m=n-1
// D.树上问题
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
int n,q,tot,head[10005],d[10005],dep[10005],p[10005][25];
struct node {
int v, w, next;
}a[10005 << 1];
void dfs(int x, int pre){
p[x][0] = pre;
dep[x] = dep[pre] + 1;
for (int i=1; i<=20; i++) p[x][i] = p[p[x][i - 1]][i - 1];
for (int i=head[x]; i!=-1; i=a[i].next){
int v = a[i].v;
if(v == pre) continue;
d[v] = d[x]+a[i].w;
dfs(v, x);
}
}
int LCA(int x, int y){
if(dep[x] < dep[y]) swap(x, y);
for (int i=20; i>=0; i--)
if(dep[p[x][i]] >= dep[y]) x=p[x][i];
if(x == y) return x;
for (int i=20; i>=0; i--)
if(p[x][i] != p[y][i]) x=p[x][i], y=p[y][i];
return p[x][0];
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d %d", &n, &q);
for (int 1, u, v, w; i<n; i++){
scanf("%d %d %d", &u, &v, &w);
a[tot] = node{v, w, head[u]}, head[u] = tot++;
a[tot] = node{u, w, head[v]}, head[v] = tot++;
}
dfs(1, 0);
for (int i=1, u, v; i<=q; i++){
scanf("%d %d", &u, &v);
printf("%d\n", d[u] + d[v] - 2 * d[LCA(u, v)]);
}
return 0;
}
问题 G: 居合
题目描述
虚拟世界要举行一个剑术大会,小Y和小X进入了决赛,决赛进行k个回合。
决赛赛制为地上有n个木桩子,每次小Y或者小X能砍其中一个柱子,他们可以选择砍掉柱子的一截(整数高度)或者是直接砍掉柱子。
每回合中,谁砍掉了最后一个柱子,谁将赢下这个回合。
机制的小Y钞能力买通了裁判,每局都让小Y先出手
输入
第一行一个数k组,
接下来每组第一行为一个整数n,第二行为n个整数,分别为每个柱子的高度h
输出
对于每个回合,如果小Y一定能赢,输出“GG”,若小Y不一定能赢,输出"gl hf"。
样例输入
3
2
1 3
2
3 3
5
1 2 3 4 5
样例输出
GG
gl hf
GG
提示
对于100%的数据:1<=k<=10,1<=n<=1001<=n<=1001<=n<=100,1<=h<=10001<=h<=10001<=h<=1000
题解:
先来讲一下异或运算:
参加运算的两个数据,按二进制位进行“异或”运算。
运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0;
即:参加运算的两个对象,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。
例如:9^5可写成算式如下: 00001001^00000101=00001100 (十进制为12)可见9^5=12
下面来引用一下大佬的证明:
// G.博弈论
#include <bits/stdc++.h>
using namespace std;
int k,n,a[10100];
int main()
{
cin>>k;
while(k--)
{
scanf("%d",&n);
int check=0;
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
check^=a[i];
}
if(!check) printf("gl hf\n");
else printf("GG\n");
}
return 0;
}