题目链接:http://codeforces.com/contest/1173
Problem A
按照题意暴力做即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#define debug(x) printf("----Line%s----\n",#x)
#define ll long long
using namespace std;
const int N = 1e5+5;
int x,y,z;
int main()
{
while (~scanf("%d %d %d",&x,&y,&z)){
if (x>z+y) printf("+\n");
else if (y>x+z) printf("-\n");
else if (x==y && z==0) printf("0\n");
else printf("?\n");
}
return 0;
}
Problem B
棋子按顺序下右下右下右,
或者右下右下右下。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#define debug(x) printf("----Line%s----\n",#x)
#define ll long long
using namespace std;
const int N = 1e5+5;
int n;
int main()
{
while (~scanf("%d",&n)){
printf("%d\n",n/2+1);
int nowx=1,nowy=1;
for (int i=1;i<=n;i++){
if (i%2==1) printf("%d %d\n",nowx++,nowy);
else printf("%d %d\n",nowx,nowy++);
}
}
return 0;
}
Problem C
题目中的操作是先放一张在底部,再取最顶上的
(一)首先最蠢的办法是把所有有数字的牌全部拿到手里,再一张张叠上去,所以最多也只需2n次操作。
(二)实际上还可以优化,对于在堆中的每张有数字的牌,假设上面数字为i,它在堆中位置为pi,那么在获得它之前的时间最明智的就是先把1,2,3...,i-1(假设都在手中)放上去“迎接”i的到来,当然放前i-1张之前的空白期就放空牌。
Example:
比如数字为4,在第七个位置,那么要取走它你要先逐渐往堆的底下塞七张牌,前四张塞空白牌,后三张塞1,2,3
也就是要正确放置,需要pi-(i-1)的缓冲时间(放空白)。
由此引出这个式子:pi -(i-1)+ n
当我们取得这个式子的最大值时,就是答案。
证明:
①证明拿走i之前,前i-1张已经在手里。
pi-i+1+n > pj-j+1+n
即pi-i > pj-j
若j<i,则pj必定小于pi
②证明大于i的牌一定可以及时到手中,每张牌x及时到手中的条件是缓冲时间(放空白牌)大于px-x+1,我们都取了最大值,所以显然成立。
由此,证毕。
其实(一)所说就是缓冲时间为n的最坏情况
(三)特殊判断堆中初始是xxxxx1234类似这样的状况(也就是一段后缀已经符合要求)
代码中注释着Part1,Part2的部分
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 2e5+5;
int a[N],b[N],p[N];
int main()
{
int n;
int pos1=0;
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",a+i);
for (int i=1;i<=n;i++){
scanf("%d",b+i);
p[b[i]]=i;
if (b[i]==1) pos1 = i;
}
int ans = n;
///特判Part 1
bool flag = true;
for (int i=pos1+1;i<=n;i++)
if (b[i]!=i-pos1+1){
flag = false;
break;
}
///特判Part 2
int now_right = n-pos1+1;
if (flag){
for (int i=now_right+1;i<=n;i++)
if (p[i]>=i-now_right){
flag = false;
break;
}
}
///输出答案
if (flag){
printf("%d\n",ans-now_right);
}
else {
for (int i=1;i<=n;i++)
ans = max(ans,p[i]-i+1+n);
printf("%d\n",ans);
}
return 0;
}
总结:
①思维题的思考,也是建立在先有暴力再在这个基础上优化。所以先想最基础的暴力,别考虑各种杂七杂八的坑数据。
Problem D
先固定一号节点。
然后全排列所有直接相连的子节点。(也就是子节点个数的阶乘)
每个子节点代表其对应的一棵子树,当这棵子树在这个圈的连续一段上时,就不会有交叉。(自己画画把,可以先把直接相领的子节点画出来,接下来试试将子树的子节点伸到其他子树的段里,发现一定会交叉)
这个图可以加深对我说的全排列的理解
对于每棵子树分配的空间,对子树根节点和直接相连子节点全排列...就这样递归下去。(也就是(deg[i]+1)的阶乘)
然后最后再乘以n,因为1节点有n个位置可以选择。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#define debug(x) printf("----Line%s----\n",#x)
#define ll long long
#define mod 998244353
using namespace std;
const int N = 2e5+5;
struct node///链式前向星
{
int to,last;
}edge[N<<1];
int head[N],id;
void add(int u,int v){edge[id].to = v; edge[id].last = head[u]; head[u] = id++;}
int son[N];
ll ans,jc[N];
void getjc()///预处理阶乘
{
jc[0] = 1;
for (ll i=1;i<=200000;i++){
jc[i] = jc[i-1]*i%mod;
}
}
void getson(int now,int pre){///获取子节点个数,pre表示父节点
for (int i=head[now];i!=0;i=edge[i].last){
int v = edge[i].to;
if (v!=pre){
son[now]++;
getson(v,now);
}
}
}
void dfs(int now,int pre)///求答案
{
if (now==1) ans = (ans*jc[son[1]])%mod;
else ans = (ans*jc[son[now]+1])%mod;
for (int i=head[now];i!=0;i=edge[i].last){
int v = edge[i].to;
if (v!=pre){
dfs(v,now);
}
}
}
int main()
{
int n,u,v;
getjc();
ans = 1;
id = 1;
scanf("%d",&n);
for (int i=0;i<n-1;i++){
scanf("%d %d",&u,&v);
add(u,v);
add(v,u);
}
getson(1,1);
dfs(1,1);
printf("%I64d\n",ans*n%mod);
return 0;
}
大大大总结:
①不会做记得跳,成功吸取教训,这次加了200多分应该可以慢慢掉了。
②思维题想不出当前最优解,先想最不优的最优解。(欲优化先暴力)