*打铁了,我确实不够强,现场队伍也没有强到可怕的地步。*?
但也可惜,假板子害人不浅,还有我们三个没看完所有可做题,
大概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;
}