19 ccpc 秦皇岛

*打铁了,我确实不够强,现场队伍也没有强到可怕的地步。
但也可惜,假板子害人不浅,还有我们三个没看完所有可做题,
*?
大概yy补一下可以做的题

Problem D. Decimal

1 / n 1/n 1/n是否为无限小数,
我反正懒得想,直接算 1 0 1 e 5 m o d   n 10^{1e5}mod\ n 101e5mod n
然后Yes和No反了,三个人一起看,都没看出来,样例都没过就交了…

Problem F. Forest Program

仙人掌上找出所有环的问题。
可以lca,可以点双。
dfs的做法的造不出来,点双场上学的也不会 。

点双
/*   Author : Rshs
 *   Data : 2019-10-02-14.51
 */
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const double eps = 1e-10;
const LL mod = 998244353;
const int MXN = 1e6+5;
vector<int>g[MXN],bcc[MXN];
/*                点双分量         */
int dfn[MXN],iscut[MXN],dfs_clock,bcc_cnt;
/*先序序号   割点否     时间戳    点双数量*/
stack<int>sk;
/* 暂存儿子*/
int tarjan(int u,int fa){
    int lowu=dfn[u]=++dfs_clock;
    int child=0;//记录u的儿子数
    for(auto v:g[u]){
        if(!dfn[v]){
            sk.push(v);//搜索到就入栈
            child++;
            int lowv=tarjan(v,u);
            lowu=min(lowu,lowv);
            if(lowv>=dfn[u]){ //u是割点,u及v为根的子树是点双
                bcc_cnt++;
                iscut[u]=1;
                while(sk.top()!=v) bcc[bcc_cnt].push_back(sk.top()),sk.pop();
                bcc[bcc_cnt].push_back(sk.top()),sk.pop();
                bcc[bcc_cnt].push_back(u);

            }
        }
        else if(v!=fa&&dfn[v]<dfn[u]){//保证反向边,保证每条边只访问一次
            lowu=min(lowu,dfn[v]);  //连到了u的祖先(反向边
        }
    }
    if(fa<0&&child==1) iscut[u]=0; //处理根结点
    return lowu;
}
void bccGet(int n){
    for(int i=1;i<=n;i++)bcc[i].clear(),dfn[i]=iscut[i]=0;
    while(!sk.empty())sk.pop();
    for(int i=1;i<=n;i++) if(!dfn[i])tarjan(i,-1);
}
LL quickPow(LL x,LL y,LL Mo){
    LL re=1,jin=x;
    while(y){
        if(y%2==1) re=re*jin%Mo;
        y=y>>1;jin=jin*jin%Mo;
    }
    return re;
}
int main(){
    int n,m;cin>>n>>m;
    for(int i=1;i<=m;i++){
        int sa,sb;scanf("%d %d",&sa,&sb);
        g[sa].push_back(sb);g[sb].push_back(sa);
    }
    bccGet(n);
    LL ans=1;
    for(int i=1;i<=bcc_cnt;i++) {
        if(SZ(bcc[i])>2) ans=ans*(quickPow(2,SZ(bcc[i]),mod)-1)%mod,m-=SZ(bcc[i]);
    }
    ans=ans*quickPow(2,m,mod)%mod;
    cout<<ans;
    return 0;
}


lca
/*   Author : Rshs
 *   Data : 2019-09-23-17.38
 */
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const double eps = 1e-10;
const LL mod = 998244353;
const int MX = 3e5+5;
const int MXLOG = 22;
struct no{LL w,v;};
vector<no>g[MX];
int bei[MX][MXLOG],de[MX];
LL dis[MX];//到根的距离
void dfs(int v,int p){//所有都是向上走2^k步 所代表的含义
    for(auto i:g[v]) {
        if(i.v!=p){
            de[i.v]=de[v]+1;bei[i.v][0]=v;dis[i.v]=dis[v]+i.w;
            dfs(i.v,v);
        }
    }
}
void initLCA(int nn){
    bei[0][0]=-1;de[0]=0;dfs(0,-1);
    for(int k=0;k<MXLOG-1;k++){  //-1!!!
        for(int v=0;v<=nn;v++){
            if(bei[v][k]<0)bei[v][k+1]=-1;
            else bei[v][k+1]=bei[bei[v][k]][k];
        }
    }
}
int LCA(int u,int v){
    if(de[u]>de[v]) swap(u,v);
    for(int k=0;k<MXLOG;k++){
        if((de[v]-de[u])>>k&1) v=bei[v][k];
    }
    if(u==v) return u;
    for(int k=MXLOG-1;k>=0;k--){
        if(bei[u][k]!=bei[v][k]){u=bei[u][k],v=bei[v][k];}
    }
    return bei[u][0];
}
LL quDis(int u,int v){
    int lca=LCA(u,v);
    return dis[u]+dis[v]-2LL*dis[lca];
}
LL quickPow(LL x,LL y,LL Mo){
    LL re=1,jin=x;
    while(y){
        if(y%2==1) re=re*jin%Mo;
        y=y>>1;jin=jin*jin%Mo;
    }
    return re;
}
vector<PII> qu;
int fa[MX];
set<int>SET;
int found(int x){
    if(x==fa[x])return x;
    return fa[x]=found(fa[x]);
}
int main(){
    int n,m;scanf("%d%d",&n,&m);
    qu.clear();SET.clear();
    for(int i=0;i<=n;i++)g[i].clear();
    for(int i=1;i<=n;i++)fa[i]=i; //并查集缩点然后链接超级源点,然后森林变成一棵树
    for(int i=1;i<=m;i++){
        int sa,sb;scanf("%d %d",&sa,&sb);
        int fsa=found(sa),fsb=found(sb);
        if(fsa==fsb) {qu.push_back(MP(sa,sb));continue;} //出现环了,之后计算两点距离
        fa[fsa]=fsb;
        g[sa].push_back(no{1,sb});
        g[sb].push_back(no{1,sa});
    }
    for(int i=1;i<=n;i++)SET.insert(found(i));
    for(auto i:SET){
        g[0].push_back(no{1,i});g[i].push_back(no{1,0});
    }
    initLCA(n);
    int cn=m;
    LL ans=1;
    for(auto i:qu){
        int gg=quDis(i.FI,i.SE)+1;
        cn-=gg;
        ans=ans*(quickPow(2,gg,mod)-1)%mod;
    }
    cout<<(ans*quickPow(2,cn,mod)%mod+mod)%mod<<'\n';
    return 0;
}

Problem I. Invoker

根据字符串线性根据所有状态暴力分类模拟转移。

把QWE分别hash成数字012
dp[][3][3]  i位置,字符对应的连击第二位的数值,字符对应连击的第三位的数值,    的最小值
void main()
{
	string s;
	cin>>s;
	multimap<char,set<int>>,手敲出来字符技能对应的连击
	for(i:从左到右扫字符串){
		如果s[i]==s[i-1] .......
		for(枚举当前字符对应的连击的第二位){
			for(枚举当前字符对应的连击的第三位){
				判断当前此状态是否合理.......
				从i-1的位置转移,选取前面连击0位
				从i-1的位置转移,选取前面连击1位
				从i-1的位置转移,选取前面连击2}
		}
	}

Problem J. MUV LUV EXTRA

其实认认真真看懂题,这题是好写的
求的是后缀循环节,倒过来求前缀循环节,那么就是kmp
因为给的那个公式,如果循环节确定,那么肯定字符串越长越好,也就是每次计算的字符串的右端点一定是 l e n ( s ) − 1 len(s)-1 len(s)1,也就是后缀
那么这题就是求循环节的裸题
注意 n e [ 1 ] = 1 ne[1]=1 ne[1]=1

再次想说,假板子害人,至少浪费1个多小时,没来及细看这题

/*   Author : Rshs
 *   Data : 2019-09-23-20.32
 */
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const double eps = 1e-10;
const LL mod = 1e9+7;
const int MX = 1e7+10;

char ss[MX],s[MX];int plen;
int ne[MX];
void GetNext(char *p,int *net){
    net[0]=-1;int k=-1,j=0;
    while(j<plen){
        if(k==-1||p[j]==p[k]) ++k,++j,net[j]=k;
        else k=net[k];
    }
}
int main(){
    LL a,b;
    while(cin>>a>>b){
        scanf("%s",ss);int le=strlen(ss);
        int pp;plen=0;
        for(int i=0;i<le;i++){
            if(ss[i]=='.'){pp=i+1;break;}
        }
        for(int i=le-1;i>=pp;i--){
            s[plen++]=ss[i];
        }
        GetNext(s,ne);
        LL ans=a-b;
        for(LL i=2;i<=plen;i++){
            ans=max(ans,a*(i)-b*(i-(LL)ne[i]));
        }
        cout<<ans<<'\n';
    }
    return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值