Codeforces Round #482 (Div. 2)(A+B+C+D)

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值