A题 题目链接 (签到+数学)
题意:给一个园,给定一个大于等于0的数字n,问把圆分成n+1块全等的扇形,至少需要几根直线。
思路: n+1为偶数的时候,就是(n+1)/2 ,奇数的时候,就是n+1
当n+1为1的时候比较特殊,此时不需要直线,答案是0
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=3000;
const double eps=1e-5;
const int maxn=2e5+50;
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
LL n;
cin>>n;
n++;
if(n==1)
{
puts("0");
return 0;
}
if(n&1)//分成奇数份
{
cout<<n;
}
else
{
cout<<n/2;
}
// system("pause");
return 0;
}
B题题目链接 (模拟+思维)
题意:给定三个字符串,每个字符串由大小写字母组成,对于每个字符串,要求进行n(n>=1)次操作,每次可以把一个字母变成另一个,定义字符串优秀值为其出现最多次数的子串出现频数,问n次操作后,3个字符串哪个的优秀值最大,不唯一则输出Draw。
思路:由贪心可知,子串肯定要取单个字符,我们统计得到这个字符串单个字母出现最高频数mmax 。 mmax<=len . len为字符串长度,
~如果mmax==len ,即:字符串仅由一个字母组成
此时如果n大于1,优秀值一定为len,因为可以x->a1>a2>a3…->x 最终可以变回 来。
但是如果 n等于1,就说明一定要损失一个,优秀值就是len-1
~如果mmax<len ,优秀值为 min(len,mmax+n)
原因:如果mmax+n<=len 此时不用解释,直接每个其它字母都变成这个字母即可
如果mmax+n>len 依然是 b->a1>a2>a3…->x
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=3000;
const double eps=1e-5;
const int maxn=1e5+50;
int n,ans;
int m[4]={0};
char s[maxn];
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
scanf("%d",&n);
for(int i=1;i<=3;i++)
{
scanf("%s",s+1);
int len=strlen(s+1);
int book[128]={0};
for(int i=1;i<=len;i++) book[s[i]]++;
int mmax=0;
for(int i=1;i<=len;i++) mmax=max(mmax,book[s[i]]);
if(mmax==len && n==1)//只变1次就肯定要损失一个
{
m[i]=len-1;
}
else m[i]=min(len,mmax+n);
ans=max(ans,m[i]);
}
int cnt=0,index=0;
for(int i=1;i<=3;i++)
{
if(ans==m[i])
{
cnt++;
index=i;
}
}
if(cnt!=1)
{
puts("Draw");
}
else
{
if(index==1)
{
puts("Kuro");
}
else if(index==2)
{
puts("Shiro");
}
else
{
puts("Katie");
}
}
// system("pause");
return 0;
}
C题 题目链接 (DFS+组合数学)
题意:给定一棵树,给定特殊节点x y 问有多少条简单路径满足:不会先经过x再经过y。 (1->2 2->1 视为两条不同路径)
思路:首先树的所有简单路径为n*(n-1) ,我们再求出不满足条件的路径,减去就是答案。
不满足条件的路径怎么求呢?如上图,1为x 3为y,容易看出,如果某条路径从x或者x左边的节点出发抵达y或者y右边的节点 ,那么它一定不满足。
所以我们先从x 出发,进行dfs,遇到y 就不搜,记录搜到的节点总数 ,最终 n-搜到节点总数 就是y以及y右边(指图上的右边,本质是y->x的相反方向)的节点总数,记为numx。
同理求出numy
最终n*(n-1)-numx*numy就是答案
注:记得开long long = =、
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=3000;
const double eps=1e-5;
const int maxn=3e5+50;
//给定一棵树 特殊节点x y 问有多少条简单路径 不会先经过x再经过y
struct
{
int to,next;
}edge[maxn*2];
int head[maxn],cnt=1;
int n,x,y;
void add(int from,int to)
{
edge[cnt].to=to;
edge[cnt].next=head[from];
head[from]=cnt++;
}
int dfs(int rt,int par,int no)
{
int ans=1;
for(int i=head[rt];i;i=edge[i].next)
{
int to=edge[i].to;
if(to==par || to==no) continue;
ans+=dfs(to,rt,no);
}
return ans;
}
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
scanf("%d%d%d",&n,&x,&y);
for(int i=1;i<n;i++)
{
int v,u;
scanf("%d%d",&v,&u);
add(v,u),add(u,v);
}
int numx=n-dfs(y,y,x);
int numy=n-dfs(x,x,y);
printf("%lld",1ll*n*(n-1)-1ll*numx*numy);
// system("pause");
return 0;
}
D题 题目链接 (set+数论+思维)
题意:给定q个指令,给定一个空数组,当指令为1 ,输入x并插入数组,
当指令为2 输入x k s,找出数组中一个数v,满足gcd(x,v)%k为0 ,并且x+v<=s 。 如果有多个 ,输出使得v xor x最大的v
思路:
gcd(x,v)%k为0 等价于 x%k为0 并且 v%k为0
用set[i]存所有可以整除i的数组元素
当指令为2 输入x k s,在set[k]中二分找到满足x+v<=s的v的上界 然后遍历求异或值。
但是仅仅是这也样的话会tle,这里有个优化技巧。
——x XOR v 的最大值不会超过x+v ,记这个最大值为mmax
我们利用这个结论,把v从大到小遍历,如果某次进入循环时发现,mmax>x+*it (it为当前位置的迭代器),说明此时可以break了,因为以后的遍历中,异或值最大也只能是x+*it,小于当前这个mmax。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=30000000;
const double eps=1e-5;
const int maxn=1e5+50;
//题意:给定q个指令,一个空数组,指令为1 ,输入x并插入数组,
//指令为2 输入x k s,找出数组中一个数v,满足gcd(x,v)%k==0 并且x+v<=s 如果有多个 ,输出使得v xor x最大的v
//思路:用set[i]存 目前所有可以被i整除的数组元素
//当指令为2 输入x k s,在set[k]中找到到所有x+v<=s的v 然后求异或值
set<int> st[maxn];//所有可以被i整除的数组元素
inline void div(int x)//处理x的每个因数
{
int X=(int)sqrt(x);
for(register int i=1;i<=X;i++)
{
if(x%i==0)
{
st[i].insert(x);
st[x/i].insert(x);
}
}
}
inline int find(int x,int k,int s)
{
if(x%k) return -1;//gcd(x,v)%k==0 等价于 x%k==0 v%k==0
if(st[k].empty()) return -1;//不存在这样的v
set<int>::iterator it=st[k].upper_bound(s-x);//找到第一个最大的 如果没找到返回的是begin迭代器
if(it==st[k].begin()) return -1;//全部的v都会使得v+x>s
it--;//*it此时是大于s-x的
int mmax=-1,ans=0;
for(;it!=st[k].begin();it--)
{
int v=*it;
if(mmax>x+v) break;
if((v^x)>mmax)
{
mmax=v^x;
ans=v;
}
}
if((*it^x)>mmax)//.begin()迭代器处的元素没有判断
{
ans=*it;
}
return ans;
}
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
int q,k,x,s;
scanf("%d",&q);
for(register int i=1;i<=q;i++)
{
int cmd,x,k,s;
scanf("%d",&cmd);
if(cmd==1)
{
scanf("%d",&x);
div(x);
}
else
{
scanf("%d%d%d",&x,&k,&s);
printf("%d\n",find(x,k,s));
}
}
// system("pause");
return 0;
}