题面:http://acm.hdu.edu.cn/downloads/CCPC2018-Hangzhou-ProblemSet.pdf
Problem A. Super-palindrome:
每个奇数长度的子串都是回文,有两种情况:
1. aaaaaaa 都是相同字符
2.ababababab 两个不同的字符一直交替。
枚举每两个字符即可。
代码:
#include<bits/stdc++.h>
using namespace std;
char t[105];
int k[27];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%s",t+1);
int len=strlen(t+1),ans=1e9;
for(int i=0;i<26;i++)
{
for(int j=0;j<26;j++)
{
int num=0;
for(int k=1;k<=len;k++)
{
if(k%2&&t[k]-'a'!=i) num++;
if(k%2==0&&t[k]-'a'!=j) num++;
}
ans=min(ans,num);
}
}
cout<<ans<<endl;
}
return 0;
}
Problem B. Master of Phi:
把欧拉函数φ(d)=d*带入到上式得
d可以写成p1^(x1) * p2^(x2) * p3^(x3) ..... 把xi==0看做一种状态,xi>0看做一种状态,
则一共有2^(20)种状态,每种状态的值相同,对于每种状态的每个不为0的
xi,都有1~qi种选择,因此每种状态有所有不为0的qi之积。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
ll p[22],q[22],inv[22],ans;
int m;
ll pow1(ll a,ll b)
{
ll r=1;
while(b)
{
if(b&1) r=r*a%mod;
a=a*a%mod;
b/=2;
}
return r;
}
void dfs(int i,ll cnt)
{
if(i==m+1)
{
ans=(ans+cnt)%mod;
return;
}
dfs(i+1,cnt);
dfs(i+1,cnt*q[i]%mod*(p[i]-1)%mod*inv[i]%mod);
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%d",&m);
ll n=1;
ans=0;
for(int i=1;i<=m;i++)
{
scanf("%lld%lld",&p[i],&q[i]);
n=n*pow1(p[i],q[i])%mod;
inv[i]=pow1(p[i],mod-2);
}
dfs(1,n);
printf("%lld\n",ans);
}
return 0;
}
Problem C. Hakase and Nano:
若H先手,当且仅当n%3==1,且所有数都为1时H才会输。
对应,H后手时只有Nano能把状态转化成n%3==n,且每个数都为1才会获胜。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e6+10;
int a[maxn];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n,d,ans=0;scanf("%d%d",&n,&d);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]>1) ans++;
}
if(d==1)
{
if(n<=2) printf("Yes\n");
else if(n%3==0&&ans==0) printf("No\n");
else printf("Yes\n");
}
else
{
if(n==1) printf("No\n");
else if(n==2) printf("Yes\n");
else if(n%3==1&&ans<=1) printf("No\n");
else if(n%3==0&&ans==1) printf("No\n");
else printf("Yes\n");
}
}
return 0;
}
Problem D. Master of Random:
对于每个i,所有的j>i,都有1/i的概率在以i为根的子树内,计算每个点的期望然后求平均即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+10;
const ll mod=998244353;
ll a[maxn];
ll pow1(ll a,ll b)
{
ll r=1;
while(b)
{
if(b&1) r=r*a%mod;
a=a*a%mod;
b/=2;
}
return r;
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n;scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
ll sum=0,ans=0;
for(int i=n;i>=1;i--)
{
ans+=sum*pow1(1LL*i,mod-2)%mod;
(ans+=a[i])%=mod;
(sum+=a[i])%=mod;
}
cout<<ans*pow1(1LL*n,mod-2)%mod<<endl;
}
return 0;
}
Problem E. Master of Subgraph:
考虑以一个点为根,且必须选根的方案,选择一个点则必须选择这个点的父亲节点,因此此节点的状态就是
他的父节点S[fa]<<w[i],回溯时把子节点的状态添加到父节点中S[fa] | =S[i]。然后点分治处理。
复杂度n*m*logn,因为是0,1,状态的转化,因此可以用bitset优化转移。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
bitset<maxn>S[3003],ans;
vector<int>G[3003];
int son[3003],root,now_size;
int w[3003],n,m;
bool vis[3003];
void get_root(int v,int fa,int SIZE)
{
son[v]=1;
int ma=0;
for(int i=0;i<G[v].size();i++)
{
int to=G[v][i];
if(to==fa||vis[to]) continue;
get_root(to,v,SIZE);
son[v]+=son[to];
ma=max(ma,son[to]);
}
ma=max(ma,SIZE-son[v]);
if(ma<now_size)
{
now_size=ma;
root=v;
}
}
void cal(int v,int fa)
{
for(int i=0;i<G[v].size();i++)
{
int to=G[v][i];
if(to==fa||vis[to]) continue;
S[to]=(S[v]<<w[to]);
cal(to,v);
S[v]|=S[to];
}
}
void dfs(int v,int fa)
{
vis[v]=1;
S[v].reset();
S[v][w[v]]=1;
cal(v,0);
ans|=S[v];
for(int i=0;i<G[v].size();i++)
{
int to=G[v][i];
if(vis[to]) continue;
get_root(root=to,0,now_size=son[to]);
dfs(root,0);
}
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
for(int i=0;i<3003;i++) G[i].clear();
memset(vis,0,sizeof(vis));
ans.reset();
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int x,y;scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
get_root(root=1,0,now_size=n);
dfs(root,0);
for(int i=1;i<=m;i++)
{
if(ans[i]) printf("1");
else printf("0");
}
printf("\n");
}
return 0;
}
Problem J. Master of GCD:
这题上来第一眼想线段树区间乘法,但是每次乘2最后数很大需要取模,但是先取模后求GCD和先求GCD后取模结果不同。
考虑到最后n个数都可以写成(2^x)*(3^y)的形式。取n个数中最小的x和y,设为xx,yy,则(2^xx)*(3^yy)便是所有数的最大公约数。
剩下的便是线段树维护区间加法。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+10;
const ll mod=998244353;
struct node
{
ll v1,v2,lz1,lz2;
}tree[maxn<<2];
ll pow1(ll a,ll b)
{
ll r=1;
while(b)
{
if(b&1) r=r*a%mod;
a=a*a%mod;
b/=2;
}
return r;
}
void push_down(int l,int r,int rt)
{
int mid=(l+r)/2;
if(tree[rt].lz1)
{
tree[rt<<1].v1+=(mid-l+1)*tree[rt].lz1;
tree[rt<<1|1].v1+=(r-mid)*tree[rt].lz1;
tree[rt<<1].lz1+=tree[rt].lz1;
tree[rt<<1|1].lz1+=tree[rt].lz1;
tree[rt].lz1=0;
}
if(tree[rt].lz2)
{
tree[rt<<1].v2+=(mid-l+1)*tree[rt].lz2;
tree[rt<<1|1].v2+=(r-mid)*tree[rt].lz2;
tree[rt<<1].lz2+=tree[rt].lz2;
tree[rt<<1|1].lz2+=tree[rt].lz2;
tree[rt].lz2=0;
}
}
void update(int L,int R,int C,int l,int r,int rt)
{
if(l>=L&&r<=R)
{
if(C==2)
{
tree[rt].v1+=(r-l+1);
tree[rt].lz1+=1;
}
else
{
tree[rt].v2+=(r-l+1);
tree[rt].lz2+=1;
}
return;
}
push_down(l,r,rt);
int mid=(l+r)/2;
if(L<=mid) update(L,R,C,l,mid,rt<<1);
if(R>mid) update(L,R,C,mid+1,r,rt<<1|1);
}
node query(int L,int l,int r,int rt)
{
if(l==r) return tree[rt];
push_down(l,r,rt);
int mid=(l+r)/2;
if(L<=mid) return query(L,l,mid,rt<<1);
else return query(L,mid+1,r,rt<<1|1);
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
for(int i=0;i<4*maxn;i++)
tree[i].v1=tree[i].v2=tree[i].lz1=tree[i].lz2=0;
int n,m;scanf("%d%d",&n,&m);
while(m--)
{
int l,r,k;scanf("%d%d%d",&l,&r,&k);
update(l,r,k,1,n,1);
}
ll num1=1e8,num2=1e8;
for(int i=1;i<=n;i++)
{
node e=query(i,1,n,1);
num1=min(num1,e.v1);
num2=min(num2,e.v2);
//printf("%lld %lld\n",e.v1,e.v2);
}
ll ans=pow1(2,num1)*pow1(3,num2)%mod;
printf("%lld\n",ans);
}
return 0;
}
Problem K. Master of Sequence:
a[i]只有1000,可以按a[i]归类,f[i][j]表示a[i]==i,且b[i]%a[i]>=j的有多少个。
, - ret 即为当前t==0时的值,二分t,
,
然后对于每个 t%a[i]<b[i]%a[i],cnt=cnt-1;
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+10;
ll a[maxn],b[maxn],f[1005][1005];
ll ans,len[1005],ret;
int n,m;
bool check(ll x,ll k)
{
ll cnt=0;
for(int i=1;i<=1000;i++)
{
if(!len[i]) continue;
cnt+=f[i][0]*(x/i);
cnt-=f[i][x%i+1];
}
if(cnt-ret>=k) return 1;
return 0;
}
ll solve(ll k)
{
ll l=1,r=1e13,cnt;
while(l<=r)
{
ll mid=(l+r)/2;
if(check(mid,k)) cnt=mid,r=mid-1;
else l=mid+1;
}
return cnt;
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
memset(f,0,sizeof(f));
memset(len,0,sizeof(len));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
ret=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&b[i]);
len[a[i]]++; f[a[i]][b[i]%a[i]]++;
ret+=b[i]/a[i];
}
for(int i=1;i<=1000;i++)
for(int j=i-1;j>=0;j--)
f[i][j]+=f[i][j+1];
while(m--)
{
int op,x,y,z;scanf("%d",&op);
if(op==1)
{
scanf("%d%d",&x,&y);
for(int i=b[x]%a[x];i>=0;i--) f[a[x]][i]--;
len[a[x]]--;
ret-=b[x]/a[x];
a[x]=y;
len[a[x]]++;
ret+=b[x]/a[x];
for(int i=b[x]%a[x];i>=0;i--) f[a[x]][i]++;
}
else if(op==2)
{
scanf("%d%d",&x,&y);
for(int i=b[x]%a[x];i>=0;i--) f[a[x]][i]--;
ret-=b[x]/a[x];
b[x]=y;
ret+=b[x]/a[x];
for(int i=b[x]%a[x];i>=0;i--) f[a[x]][i]++;
}
else
{
scanf("%d",&z);
printf("%lld\n",solve(1LL*z));
}
}
}
return 0;
}