2017 icpc 沈阳
链接
就这几个题目来说感觉都偏技巧性,考验思维能力。
Little Boxes
输出a+b+c+d JAVA就行
Rabbits
分析题目得到
答案最多就是a[n]-a[1]-n+1 即最大值和最小值之间的空隙,那么由于每次移动一个点时,会有k个间隙被浪费掉(k为与它最近的点之间的间隙)。那我们每次移动的时候都贪心的选择k较小的,然后考虑把这只兔子放在哪里?仔细想想能够发现既然我们每次都要选择k较小的,那是否可以利用上一只移动的兔子来构造这个k使它最小。答案是肯定的。并且除第一次外,每次的k都是0
所以我们统计一下总间隙数量后减去一开始消耗的间隙较小的那一个O(1)输出就行了
#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> PII;
typedef pair<LL,LL> PLL;
typedef vector<int> VI;
const int maxn = 2e6+6;
const int N = 5e3+5;
const int mod = 998244353;
const LL inf = 0x3f3f3f3f3f3f3f3f;
struct node{
int num,id;
bool operator <(const node &k) const {
return num<k.num;
}
};
priority_queue<node>q;
int n,t,cs,a[maxn],aa[maxn],ans[maxn],vis[maxn];
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
int ans=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
printf("%d\n",a[n]-a[1]+1-n-min(a[2]-a[1]-1,a[n]-a[n-1]-1));
}
}
Heron and His Triangle
首先打表后发现前几项分别是4,14,52,…并且看到这个值增加的非常快,所以可以想到这个数列有可能是类似斐波那契数列一样的递推式,然后手动枚举一下系数就可以发现通项公式为F[n]=4*F[n-1]-F[n-2] 还是用JAVA模拟一下就好了
以后碰到这种前几项增长速度还可以但是后面的增长速度很快的数列还是要往递推式方向靠一下。
Infinite Fraction Path
首先肯定要 使第一个数字最大,并且确定起点之后的路径只有一条,但是这个最大的数字可能有很多项,我们考把他扔到队列里面,然后依次模拟队列里的每一项的下一项的最大值(也可能有很多项),在丢到队列里,重复n次。如果这样写的话,我们粗略考虑一下复杂度,发现如果当序列的所有数字全为一样的话,那整体的复杂度有可能到O(n*n)。
但其实我们手动模拟一下这个序列构成的图后可以发现,即使一开始队列有很多点,但是这些点大部分的目标点都是同一个点,也就是说队列里的不同点的数量减少的是非常快的,那么对于某一时刻,我们只需要控制不让相同位置的点都进队列,就可以得到一个看起来 正确的复杂度?其实我也不知道为什么这样剪枝的复杂度究竟是多少
#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> PII;
typedef pair<LL,LL> PLL;
typedef vector<int> VI;
const int maxn = 2e6+6;
const int N = 5e3+5;
const int mod = 998244353;
const LL inf = 0x3f3f3f3f3f3f3f3f;
struct node{
int num,id;
bool operator <(const node &k) const {
return num<k.num;
}
};
priority_queue<node>q;
int n,t,cs,a[maxn],aa[maxn],ans[maxn],vis[maxn];
int main(){
scanf("%d",&t);
while(t--){
while(!q.empty()) q.pop();
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%1d",&a[i]);
q.push({a[i],i-1});
}
for(int i=1;i<=n;i++){
ans[i]=q.top().num;
if(i==n) break;
int cnt=0;
while(q.size()){
if(q.top().num<ans[i]) break;
aa[++cnt]=q.top().id;
q.pop();
}
while(q.size()) q.pop();
for(int j=1;j<=cnt;j++){
int c=(1ll*aa[j]*aa[j]+1)%n;
if(vis[c+1]) continue;
q.push({a[c+1],c});
vis[c+1]=1;
}
for(int j=1;j<=cnt;j++){
int c=(1ll*aa[j]*aa[j]+1)%n;
vis[c+1]=0;
}
}
printf("Case #%d: ",++cs);
for(int i=1;i<=n;i++) printf("%d",ans[i]);
printf("\n");
}
}
Tree
正难则反,直接考虑染哪些点后能得到最大的边交集比较难,那不妨直接考虑最后的边交集是什么样的?容易想到最后的边交集一定是连续的,想到这就不难发现,如果一条边的两边的点的数量>=k,那么这条边一定是可以成为答案的边集中的点。
考虑枚举这些边来得到最终答案的话好像不是那么容易
那我们不妨任选一个点为根,由于每个点只有一个父亲(除根节点外),这样就可以枚举每个节点看他的子树大小与除了这颗子树外的点数是否都>=k即可
#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> PII;
typedef pair<LL,LL> PLL;
typedef vector<int> VI;
const int maxn = 2e6+6;
const int N = 5e3+5;
const int mod = 998244353;
const LL inf = 0x3f3f3f3f3f3f3f3f;
int n,k,t,sz[maxn],u,v;
VI G[maxn];
void dfs(int u,int fa){
sz[u]=1;
for(int v:G[u]){
if(v==fa) continue;
dfs(v,u);
sz[u]+=sz[v];
}
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) G[i].clear();
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
G[u].pb(v);
G[v].pb(u);
}
dfs(1,-1);
int cnt=0;
for(int i=1;i<=n;i++){
if(sz[i]>=k&&n-sz[i]>=k) cnt++;
}
printf("%d\n",cnt);
}
}
Wandering Robots
一道很有趣的概率结论题 增加了一种概率问题的姿势
Q:一个n*n矩阵,每个点有坐标0~n-1,有k个障碍物分别在xi,yi;一个人在(0,0)每秒可以往上下左右走或者留在原地,前提是不能越界和遇到障碍物。问无穷时间后这个人所在的坐标x+y>=n-1的概率
A:将每个点赋予一个度数,即他能到达的点的个数,那么时间无穷大时,x+y>=n-1的概率就为对应坐标的权值/二维矩阵的权值