J Minimum Manhattan Distance

题意:给两个圆,C1,C2,你需要在C2上选择一个点,使得到C1上面的所有点(包括圆内)的曼哈顿距离的期望值最小,求这个最小值

思路:由概率密度公式推出这个点在以C2的圆心为中心的一个正方形与圆C2的四个交点之一,因此我们映射C1为原点,然后对C2上面的四个点枚举即可

diamond:

#include "bits/stdc++.h"
using namespace std;
double juli(double x1,double  y1,double x2,double y2){
    return ::sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
void solve(){
    double x1,x2,y1,y2;
    cin>>x1>>y1>>x2>>y2;
    double x=(x1+x2)/2;
    double y=(y1+y2)/2;
    cin>>x1>>y1>>x2>>y2;
    double xc=(x1+x2)/2;
    double yc=(y1+y2)/2;
    xc-=x;
    yc-=y;
    double pi= ::acos(0)*2;
//    cout<<pi<<' ';
    double  r = juli(x1,y1,x2,y2)/2;
    auto res=[&](double du){
        return abs(r*cos(du/180*pi)+xc)+abs(r*sin(du/180*pi)+yc);
    };
    vector<double >a;
    a.push_back(45);
    a.push_back(45+90);
    a.push_back(45+180);
    a.push_back(45+270);
    double ans=1e9;
    for (int i = 0; i < 4; ++i) {
        ans=min(ans,res(a[i]));
    }
    ::printf("%.9f\n",ans);
}
signed main() {
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t=1;
    cin>>t;
    while (t--){
        solve();
    }
}

I Pa?sWorD

题意:给一个密码字符串,包括0-9,大写和小写英文字母和问号四种类型,如果是小写英文字母,这个位置可以是这个字母的大写和小写两种情况,如果是问号,那么可以是任意字符,其他情况不变,问这样的密码有多少种符合规范的密码,规范有四点要求

  • 一是至少有一个小写英文字母

  • 二是至少有一个大写英文字母

  • 三是至少有一个数字

  • 四是相邻两个字符不能相等

    思路:蛮复杂的DP,具体见代码

    diamond:

    #include "bits/stdc++.h"
    using namespace std;
    #define ll long long
    #define int long long
    int type[100005],a[100005];
    ///type 表示每一位的类型,类型包括4种,0表示数字,1表示大写数字,2表示小写数字,3表示问号
    const int mod=998244353;
    ll ans;
    int n;
    ll f[8],dp[8][62],nextdp[8][62];
    /// 因题目需要三种类型都有,f为三位二进制,001代表第一种是有的,010,代表第二种是有的,100代表第三种是有的
    /// f[i]表示的是当前状态为i的合法情况的数量之和,
    /// dp是上一层的状态,nextdp表示当前层要更新的状态
    /// 我们的当前层遍历完之后,每一个f[0~7]由每一个nextdp[0~7][0~62]的和来维护
    ///判断字符的种类
    int gettype(char g){
    if(g>='0'&&g<='9')return 0;
    if(g>='A'&&g<='Z')return 1;
    if(g>='a'&&g<='z')return 2;
    return 3;
    }
    ///把字符转化为0~62的数字
    int get(char g){
    if(g>='0'&&g<='9')return g-'0';
    if(g>='A'&&g<='Z')return g-'A'+10;
    if(g>='a'&&g<='z')return g-'a'+36;
    return 63;
    }
    void solve(){
    string s;
    cin>>n>>s;
    for (int i = 0; i <n ; ++i) {
        ///处理好种类
        type[i] = gettype(s[i]);
        a[i]    = get(s[i]);
    }
    f[0]=1;
    ///000状态方案数为1;
    for (int i = 0; i <n ; ++i) {
        if(type[i]<=2){
            ///如果不是问号,数字和大写字母直接处理即可,这一层的状态由上一层的状态推出,
            for (int j = 0; j <8 ; ++j) {
                nextdp[j|(1<<type[i])][a[i]]=(nextdp[j|(1<<type[i])][a[i]]+f[j]-dp[j][a[i]]+mod)%mod;
                ///第i位放a[i]的情况,由于相邻位置不能相同,那么就是上一层的状态为j的减去末尾为a[i]的,即可
                if(type[i]==2){
                    nextdp[j|2][a[i]-26]=(nextdp[j|2][a[i]-26]+f[j]-dp[j][a[i]-26]+mod)%mod;
                    ///第i位放a[i],类型为2,那么转为大写的,大写的就要多算一步
                }
            }
        }
        else{
            ///问号,枚举每一个情况即可
            for (int j = 0; j < 8; ++j) {
                ///分成三类
                ///问号为数字
                for (int k = 0; k <10 ; ++k) {
                    nextdp[j|1][k]=(nextdp[j|1][k]+f[j]-dp[j][k]+mod)%mod;
                }
                ///问号为大写字母
                for (int k = 10; k <36 ; ++k) {
                    nextdp[j|2][k]=(nextdp[j|2][k]+f[j]-dp[j][k]+mod)%mod;
                }
                ///问号为小写字母
                for (int k = 36; k <62 ; ++k) {
                    nextdp[j|4][k]=(nextdp[j|4][k]+f[j]-dp[j][k]+mod)%mod;
                }
            }
        }
        ///更新f以及滚动数组dp
        for (int j = 0; j <8 ; ++j) {
            f[j]=0;
            for (int k = 0; k <62 ; ++k) {
                f[j]=(f[j]+nextdp[j][k])%mod;
                dp[j][k]=nextdp[j][k];
                nextdp[j][k]=0;
            }
        }
    }
    cout<<f[7]%mod<<'\n';
    }
    signed main() {
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t=1;
    //    cin>>t;
    while (t--){
        solve();
    }
    }

G Spanning Tree

思路:我们能发现,两个连通块连边的时候肯定是按照一个连通块的根深度大的向另一个连通块连边,那么我们就可以按照题目所给出的树先将每个点的深度以及父亲节点预处理出来,然后对每一个操作只需要按照根深度大的连通块的向它的父亲节点连边即可,而这里也是能判断是否能生成题目所给出的树的依据,如果其父亲并未在相应操作中所给的另一个连通块内,那就生成不了题目所给出的树。而在能生成所给出的树的情况下,其概率很容易得到是1/(sz[u] * sz[v]),u,v表示两个连通块,我们只需要在维护并查集的时候维护其根就行,也即是维护其连通块中深度最小的点,合并时也即让深度大的连通块合并于深度小的连通块。

不要define int long long,会被卡

diamond:

#include "bits/stdc++.h"
using namespace std;
const int N=1e6+10,mod=998244353;
#define PII pair<int,int>
#define ll long long
ll qsm(ll a,ll b){
    ll res=1;
    while (b){
        if(b&1)res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
PII q[N];
int fa[N],p[N],dep[N];
int sz[N];
vector<int >g[N];
void dfs(int u,int f){
    p[u]=f;
    for (auto j:g[u]) {
        if(j==f)continue;
        dep[j]=dep[u]+1;
        dfs(j,u);
    }
}
int find (int x){
    if(x!=fa[x]){
        fa[x]=find(fa[x]);
    }
    return fa[x];
}
void solve(){
    int n;
    cin>>n;
    for (int i = 1; i <=n ; ++i) {
        fa[i]=i;
        sz[i]=1;
    }
    for (int i = 1; i <n ; ++i) {
        cin>>q[i].first>>q[i].second;
    }
    for (int i = 1; i <n ; ++i) {
        int x,y;
        cin>>x>>y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    dfs(1,0);
    ll ans=1;
    for (int i = 1; i <n ; ++i) {
        int u=find(q[i].first);
        int v=find(q[i].second);
        if(dep[u]<dep[v]){
            swap(u,v);
        }
        if(find(p[u])!=v){
            cout<<"0";
            return;
        }
        ans=ans*sz[u]%mod*sz[v]%mod;
        fa[u]=v;
        sz[v]+=sz[u];
    }
    cout<<qsm(ans,mod-2);
}
signed main() {
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t=1;
//    cin>>t;
    while (t--){
        solve();
    }
}
posted on 2023-09-21 17:06  IR101  阅读(161)  评论(0编辑  收藏  举报  来源