2017 CCPC 杭州賽區
題面: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;
}