文章目录
题目集地址 ICPC2017沈阳
F Heron and His Triangle
题目地址F Heron and His Triangle
题目大意: 三条边为t-1,t,t+1,同时满足面积为整数的三角形称为H三角形,给一个n问最小的满足H三角形的大于等于n的t值
思路:根据海伦凯勒公式推导出一个三角形面关于t的公式
3
t
2
(
t
2
−
4
)
16
\sqrt{\frac{3t^2(t^2-4)}{16}}
163t2(t2−4),我们打表找到10000以内的满足条件的t值,
4
14
52
194
724
2702
找到一个规律,a3=4*a2-a1
AC代码:
#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
void scan(__int128 &x)//输入
{
x=0;int f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
x*=f;
}
void print(__int128 x)//输出
{
if(x<0){
putchar('-');
x=-x;
}
if(x>9) print(x/10);
putchar(x%10+'0');
}
void solve()
{
__int128 n,t1,t2,t3;
scan(n);
t3=2;
for(t2 = 4;t2<n;t2=t2*4-t1)
{
t1=t3;
t3=t2;
}
print(t2);
printf("\n");
}
int main()
{
// freopen("in.txt","r",stdin);
// int t = 1;
int t;
scanf("%d",&t);
while(t--)
{
solve();
}
return 0;
}
G Infinite Fraction Path
题目地址G Infinite Fraction Path
题目大意:给出一个长度为n的由0-9构成的数字序列,下标为0 ~ n-1,从下标为i的点只能走到下标为(i*i+1)%n的点,选定一个起点走n次,记录每次走的数字,求出数字构造的最大字典序序列
思路:整体构造最大,那么每一位就尽量要最大,构造每一位必然就要搜索,将序列中最大数的下标统一存起来便于搜索,这里如果直接暴搜DFS一定会超时,为了更快的构造字符串并剪枝,采用BFS更合适
为了构造最大,每一次的选择最好是步数最小,当前位置值最大且最靠前的位置,值最大便于构造最大,位置靠前代表值在字符串的位置靠前,步数最小代表后面还有更多的可以选择的余地,那么根据这样的要求建堆即可,还要剔除重复的方案,如果两个点经过相同步数到达同一个点,那么其之后状态也一样,这样的方案只需算一次,详见代码
#include <bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=2e5+5;
int T,val[maxn],n,x,ans[maxn],nt[maxn];
char s[maxn];
struct node {
int step,pos;
bool operator<(const node&a)const {
if(step==a.step&&nt[pos]==nt[a.pos])return pos<a.pos;
//步数相等,下一步相等,判断位置
if(step==a.step)return nt[pos]<nt[a.pos];
//步数相等,判断下一步
return step>a.step;
//判断步数
}
};
signed main() {
scanf("%lld",&T);
for(int k=1; k<=T; k++) {
scanf("%lld%s",&n,s);
int mx=0;
for(int i=0; i<n; i++) {
val[i]=s[i]-'0';
mx=max(mx,val[i]);//获取最大值
ans[i]=-1;//初始化
}
for(int i=0; i<n; i++)
nt[i]=val[(i*i+1)%n];//初始化下一步
ans[n]=-1,ans[1]=mx;
int pre=-1;
priority_queue<node>q;
for(int i=0; i<n; i++)
if(val[i]==mx)q.push(node{1,i});//找到起始数字最大的位置进行BFS
while(!q.empty()) {
node t=q.top();//每次选出的必是步数最小,当前位置值最大且最靠前的位置
q.pop();
int pos=(t.pos*t.pos+1)%n,step=t.step+1;
//下一个跳转的位置和步数
if(ans[step]==-1) {//第一个跳到第step步
q.push(node{step,pos});
ans[step]=val[pos];//更新这一步的构造的字符串
pre=pos;//记录上一个位置
} else if(val[pos]==ans[step]) {//如果出现了其余位置跳转到同一个点
if(pos!=pre) {//判断是否为同一个点,不是则说明可能有别的方案
q.push(node{step,pos});
pre=pos;
}
}
if(ans[n]!=-1)break;//最后一个位置已经构造完成
}
printf("Case #%lld: ",k);
for(int i=1; i<=n; i++)printf("%lld",ans[i]);
putchar('\n');
}
return 0;
}
I Little Boxes
题目地址I Little Boxes
题目大意:有四种颜色的盒子,分别有a,b,c,d个,问一共有多少个盒子
思路:将四个数相加,注意大数即可
AC代码:
#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
void scan(__int128 &x)//输入
{
x=0;int f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
x*=f;
}
void print(__int128 x)//输出
{
if(x<0){
putchar('-');
x=-x;
}
if(x>9) print(x/10);
putchar(x%10+'0');
}
void solve()
{
__int128 a,b,c,d,res;
scan(a);
scan(b);
scan(c);
scan(d);
res = a+b+c+d;
print(res);
printf("\n");
}
int main()
{
// freopen("in.txt","r",stdin);
// int t = 1;
int t;
scanf("%d",&t);
while(t--)
{
solve();
}
return 0;
}
K Rabbits
题目地址K Rabbits
题目大意:一个坐标轴,有兔子随机分布在不同的位置,坐标都为正数,有N个兔子在数轴上,最外面的兔子可以移动到其他任意两个兔子之间的位置上,一个位置只能待一次。问最大移动的次数
思路:单独看最左端的兔子,往右边的兔子中间跳,最多跳跃的次数就是右边的兔子区间上有多少个空位就是最多跳的次数,最右端的兔子也是单独看,计算出两端的兔子的最大的跳跃次数,取最大值即可。
AC代码:
#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
int a[505];
void solve()
{
int n,res=0;
scanf("%d",&n);
for(int i = 1;i <= n;i++)
{
scanf("%d",a+i);
}
res = max(res,a[n-1]-a[1]-1-n+3);
res=max(res,a[n]-a[2]-1-n+3);
printf("%d\n",res);
}
int main()
{
// freopen("in.txt","r",stdin);
// int t = 1;
int t;
scanf("%d",&t);
while(t--)
{
solve();
}
return 0;
}
L Tree
题目地址L Tree
题目大意:一棵无根树,用k种颜色给节点涂色,将每种颜色的节点路径相连,得到边集,每种颜色有一个边集,求边集交的最大
思路:边集交最大,代表有更多的公共边,对于一条边来说,如果它是公共边,那么其左右必然都有大于等于k个节点,即使存在多条公共边也总能找到涂色方案使得这些公共边左右都能满足涂色大于等于k,直接找这样的边即可
AC代码:
#include <bits/sdc++.h>
#define int long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=2e5+10;
int Size[maxn],T,k,head[maxn],n,cnt;
struct node {
int from,to,next;
} e[maxn<<1];
void Add(int from,int to) {
e[++cnt].to=to;
e[cnt].from=from;
e[cnt].next=head[from];
head[from]=cnt;
}
void DFS(int u,int f) {//统计子树大小
Size[u]=1;
for(int i=head[u]; ~i; i=e[i].next) {
int v=e[i].to;
if(v==f)continue;
DFS(v,u);
Size[u]+=Size[v];
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >>T;
while(T--) {
int res=0;
cin >>n>>k;
memset(head,-1,sizeof(int)*(n+1));
memset(Size,0,sizeof(int)*(n+1));
for(int i=1; i<n; i++) {
int u,v;
cin >>u>>v;
Add(u,v);
Add(v,u);
}
DFS(1,0);
for(int i=1; i<=n; i++)
if(Size[i]>=k&&n-Size[i]>=k)res++;
/*这里用了一个小技巧,因为是按照序号顺序进行的,所以不会出现
重复计算的情况,举个例子,如果1-2这条边是可行的,那么不会在
计算1的时候计数,可以画图验证,如果有两条边都是可行的,但是
它们共一个点,那么必然是有一个点大于公共点,一个小于公共点,
依然可以分两次算,代码中的判断条件每次只处理了一条边
*/
cout <<res<<endl;
}
return 0;
}