2020 中国软件行业智能应用程序设计大赛
(奇怪的比赛……似乎算是IOI赛制,最高分最短运行时间排名靠前,题目有部分分)
第一题
解答
维护当前温度和当前体积,按要求修改或输出信息
官方题解
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 2020;
int n,l,a,b;
inline int read(){
int x=0, f=1;
char t=getchar();
while(t>'9'||t<'0'){if(t=='-')f=-1;t=getchar();}
while(t>='0'&&t<='9')x=x*10+t-'0',t=getchar();
return x*f;
}
int main(){
n = read();
l = read();
a=read();
b=read();
int opt;
int wendu = 0;
int tiji = 0;
for(int i=1;i<=n;i++){
opt=read();
if(opt==3){
if(wendu>=a && wendu<=b && tiji>=l) printf("%d\n", wendu);
else puts("GG");
}else{
if(opt==1){
wendu=read();
}else{
tiji = read();
}
}
}
return 0;
}
第二题
解答
在整个过程中,维护同一人手牌中同一点数的牌最多只有一张。
维护双向链表表示两人的手牌,简单起见设置首尾为“哨兵节点”。维护map<点数,指针>快速查找牌的位置。
整个过程中,每次摸牌,若已有此点数的牌,从双向链表和map中删去之前的牌;若没有,加入作为最后一张手牌。
为什么题设游戏一定在有限次数内结束是对的:
从牌堆摸牌结束时,(n-2)种4张同点数的牌必然要么两人各剩一张,要么两人各剩0张;2种3张同点数的牌必然一人1张,另一人0张。
互相抽牌过程中,抽对方的第一张牌成为自己的最后一张牌,双方的牌仿佛连成一圈,转一圈过程中每张牌必然被抽到。(n-2)种两人各剩一张,一旦其中一张被抽到,即成为两人各剩0张。最终2种,即使一人一张,下一次抽游戏结束。
官方题解
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
inline int read(){
int x=0, f=1;
char t=getchar();
while(t>'9'||t<'0'){if(t=='-')f=-1;t=getchar();}
while(t>='0'&&t<='9')x=x*10+t-'0',t=getchar();
return x*f;
}
#include<deque>
#include<map>
struct Node{
int val;
Node *lst, *nxt;
}*head[2], *tail[2];
map<int,Node*> mp[2];
int main(){
int n = read();
head[0] = new Node;
tail[0] = new Node;
head[1] = new Node;
tail[1] = new Node;
head[0]->nxt = tail[0];
tail[0]->lst = head[0];
head[1]->nxt = tail[1];
tail[1]->lst = head[1];
int a;
for(int i=1;i<=4*n-2;i++){
int id = i&1^1;
int x = read();
if(mp[id].count(x)){
mp[id][x]->nxt->lst = mp[id][x]->lst;
mp[id][x]->lst->nxt = mp[id][x]->nxt;
Node *ptr = mp[id][x];
mp[id].erase(x);
delete ptr;
}else{
Node *ptr = new Node;
ptr->val = x;
ptr->lst = tail[id]->lst;
ptr->nxt = tail[id];
ptr->lst->nxt = ptr;
ptr->nxt->lst = ptr;
mp[id][x] = ptr;
}
}
int cnt = 0;
for(int id=0;head[0]->nxt!=tail[0] && head[1]->nxt!=tail[1];id^=1){
++cnt;
Node *ptr = head[id^1]->nxt;
head[id^1]->nxt = ptr->nxt;
head[id^1]->nxt->lst = head[id^1];
int x = ptr->val;
mp[id^1].erase(x);
if(mp[id].count(x)){
mp[id][x]->nxt->lst = mp[id][x]->lst;
mp[id][x]->lst->nxt = mp[id][x]->nxt;
Node *ptr = mp[id][x];
mp[id].erase(x);
delete ptr;
}else{
Node *ptr = new Node;
ptr->val = x;
ptr->lst = tail[id]->lst;
ptr->nxt = tail[id];
ptr->lst->nxt = ptr;
ptr->nxt->lst = ptr;
mp[id][x] = ptr;
}
}
printf("%d\n",cnt);
return 0;
}
第三题
解答
随便举几个例子可以发现,根深度相同的子树如果在排列中互相纠缠,会造成代价无谓的增加(一棵树根远离所有子节点,一棵树部分子节点远离根)。
后出现的子树树根,相当于在代价计算中“越过”先出现的子树节点,后被“越过”的子树依次比先被“越过”的子树,节点数计入总代价少算一次。由排列不等式(人教版 高三数学选修4-5)反序和≤乱序和≤顺序和,节点数少的子树排在前面。
官方题解
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
inline int read(){
int x=0;
char t=getchar();
while(t>'9'||t<'0')t=getchar();
while(t>='0'&&t<='9')x=x*10+t-'0',t=getchar();
return x;
}
const int N = 600000+100;
int n,k;
#include<vector>
vector<int>e[N];
int sz[N];
bool cmp(const int x,const int y){
return sz[x] < sz[y];
}
void dfs(int u){
sz[u] = 1;
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
dfs(v);
sz[u] += sz[v];
}
sort(e[u].begin(),e[u].end(), cmp);
}
int main(){
n = read();
k=read();
for(int v=2,u;v<=n;v++){
u=read();
e[u].push_back(v);
}
dfs(1);
typedef long long LL;
LL ans = 0;
for(int u=1;u<=n;u++){
LL sum = 1;
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
ans += sum;
sum += sz[v];
}
//printf("%lld %lld\n",sum, ans);
}
printf("%lld\n", (ans+1)*k);
return 0;
}