初赛一 青云的机房组网方案
求所有互质节点距离和
1、树形dp
2、质因数的容斥原理
例如i为24,只需要考虑1,2,3,6
i为45,只需要考虑1,3,5,15
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
#define clr(x,y) memset(x,y,sizeof x)
using namespace std;
ll pri[510],vis[510],lim=500,pnt;
ll tag[510],tnt,F[510];
vector<ll>Div[510],Mul[510];
void dfs(ll cur,ll id,ll flag)
{
if(id>pnt)return;
dfs(cur,id+1,flag);
if(cur*pri[id]<=lim)
{
tag[++tnt]=cur*pri[id];
F[cur*pri[id]]=-flag;
dfs(cur*pri[id],id+1,-flag);
}
}
void getP()
{
for(ll i=2;i<=lim;i++)
{
if(!vis[i])
{
pri[++pnt]=i;
for(ll j=2;i*j<=lim;j++)vis[i*j]=1;
}
}
tag[++tnt]=1;F[1]=1;
dfs(1,1,1);
sort(tag+1,tag+tnt+1);
for(ll i=1;i<=tnt;i++)
{
for(ll j=1;j*tag[i]<=lim;j++)
{
Div[j*tag[i]].push_back(tag[i]);
Mul[tag[i]].push_back(j*tag[i]);
}
}
}
ll n,dat[10010],ans;
ll tot[510],left[10010][510],right[10010][510];
ll sum[10010][510];
namespace G
{
vector<ll>V[10010];
ll mx;
void Init()
{
for(ll i=1;i<=n;i++)V[i].clear();
clr(tot,0);clr(left,0);clr(sum,0);
ans=0;mx=0;
}
void dfs(ll u,ll fa)
{
left[u][dat[u]]++;
ll len=V[u].size();
for(ll i=0;i<len;i++)
{
ll v=V[u][i];
if(v==fa)continue;
dfs(v,u);
for(ll j=1;j<=mx;j++)left[u][j]+=left[v][j];
}
for(ll i=1;i<=tnt;i++)
{
for(int j=1;j*tag[i]<=lim;j++)
sum[u][tag[i]]+=left[u][j*tag[i]];
}
for(ll i=1;i<=mx;i++)right[u][i]=tot[i]-left[u][i];
for(ll i=1;i<=mx;i++)
{
ll siz=Div[i].size();
for(ll j=0;j<siz;j++)
{
ans+=sum[u][Div[i][j]]*F[Div[i][j]]*right[u][i];
}
}
}
void Go()
{
while(~scanf("%lld",&n))
{
Init();
for(ll i=1;i<=n;i++)scanf("%lld",&dat[i]),mx=mx>dat[i]?mx:dat[i],tot[dat[i]]++;
for(ll i=1;i<n;i++)
{
ll x,y;
scanf("%lld%lld",&x,&y);
V[x].push_back(y);V[y].push_back(x);
}
dfs(1,-1);
printf("%lld\n",ans);
}
}
}
int main()
{
getP();
G::Go();
}
初赛二 联想的显示屏校准
计算几何
初赛三 百度帐号的选取方案
输入用户名abc,如果重复就变成abcabc,求不重复时循环节最小是多少?
KMP
1、val[i][j] 从i到j的最大循环节
2、num[i][j] i开始循环节为j的子串有几个
3、sum[i][j] num数组的后缀和
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define clr(x,y) memset(x,y,sizeof x)
using namespace std;
ll val[1010][1010],num[1010][1010],sum[1010][1010];
namespace KMP
{
ll nxt[100010];
ll gnt(char b[],ll lb)
{
for(ll i=0,j=nxt[0]=-1;i<lb;)
{
j==-1||b[i]==b[j]?
nxt[++i]=++j:j=nxt[j];
}
}
};
char c[1010];
int main()
{
while(~scanf("%s",c+1))
{
ll len=strlen(c+1);
clr(val,0);
clr(num,0);
clr(sum,0);
for(ll i=1;i<=len;i++)
{
KMP::gnt(c+i,len+1-i);
for(ll j=i;j<=len;j++)
{
val[i][j]=(j-i+1)%((j-i+1)-KMP::nxt[j-i+1])==0?(j-i+1)/((j-i+1)-KMP::nxt[j-i+1]):1;
num[i][val[i][j]]++;
}
}
for(ll i=len;i>0;i--)
{
for(ll j=1;j<=len;j++)
sum[i][j]=sum[i+1][j]+num[i][j];
}
ll ans=0;
for(ll i=1;i<=len;i++)
{
for(ll j=i;j<=len;j++)
ans+=sum[j+1][val[i][j]];
}
printf("%lld\n",ans);
}
}
初赛四 遗失的支付宝密码
1、容斥 f(x)表示至少含有x个square的字符串的个数
结果为c1*m^1 + c2*m^2+ … + cn*m^n
2、打表 电脑大概需要跑20秒左右
3、要求前缀和,因为字符串长度要求不大于n,1,2也要算
4、用unsigned long long比较好
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll unsigned long long
using namespace std;
const ll mod=(ll)1<<32;
ll tab[50][50]={
{0},
{0,1},
{0,-1,1},
{0,0,-1,1},
{0,1,-1,-1,1},
{0,0,1,-1,-1,1},
{0,0,1,0,-1,-1,1},
{0,0,0,1,0,-1,-1,1},
{0,-1,1,1,0,0,-1,-1,1},
{0,0,-1,1,1,0,0,-1,-1,1},
{0,-1,0,0,2,0,0,0,-1,-1,1},
{0,0,-1,0,0,2,0,0,0,-1,-1,1},
{0,0,-1,-1,1,1,1,0,0,0,-1,-1,1},
{0,0,0,-1,-1,1,1,1,0,0,0,-1,-1,1},
{0,0,-1,0,-1,0,2,0,1,0,0,0,-1,-1,1},
{0,0,0,-1,0,-1,0,2,0,1,0,0,0,-1,-1,1},
{0,2,-3,0,-1,0,0,1,1,0,1,0,0,0,-1,-1,1},
{0,0,2,-3,0,-1,0,0,1,1,0,1,0,0,0,-1,-1,1},
{0,1,0,0,-3,0,-1,1,1,0,1,0,1,0,0,0,-1,-1,1},
{0,0,1,0,0,-3,0,-1,1,1,0,1,0,1,0,0,0,-1,-1,1},
{0,2,-1,0,-1,0,-3,0,0,2,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,2,-1,0,-1,0,-3,0,0,2,0,0,1,0,1,0,0,0,-1,-1,1},
{0,2,1,-1,-1,-1,-1,0,-3,1,1,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,2,1,-1,-1,-1,-1,0,-3,1,1,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,1,1,2,-1,-1,-2,-1,-1,0,-2,2,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,1,1,2,-1,-1,-2,-1,-1,0,-2,2,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,1,2,0,0,1,-1,-2,-2,-1,-1,1,-1,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,1,2,0,0,1,-1,-2,-2,-1,-1,1,-1,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,3,1,0,0,-1,1,-2,-2,-2,-1,0,2,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,0,3,1,0,0,-1,1,-2,-2,-2,-1,0,2,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,2,1,2,0,0,-1,-1,0,-2,-2,-2,0,1,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,0,2,1,2,0,0,-1,-1,0,-2,-2,-2,0,1,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,-3,6,1,1,-1,2,0,-1,-1,-2,0,-2,-2,-1,1,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,-3,6,1,1,-1,2,0,-1,-1,-2,0,-2,-2,-1,1,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,-2,2,1,6,-1,0,-1,2,-1,-1,-2,-2,0,-2,-1,0,0,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,-2,2,1,6,-1,0,-1,2,-1,-1,-2,-2,0,-2,-1,0,0,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,-4,4,0,4,0,5,-2,0,-1,1,-1,-2,-2,-2,0,-1,0,-1,0,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,-4,4,0,4,0,5,-2,0,-1,1,-1,-2,-2,-2,0,-1,0,-1,0,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,-4,4,-2,5,1,2,0,4,-2,0,-2,1,-2,-2,-2,-2,1,0,-1,-1,0,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,-4,4,-2,5,1,2,0,4,-2,0,-2,1,-2,-2,-2,-2,1,0,-1,-1,0,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,-7,7,-3,6,-2,5,0,2,-1,4,-2,-1,-2,0,-2,-2,-2,-1,2,-1,-1,-1,0,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1}
};
ll pow(ll p,ll q)
{
ll ret=1;
while(q)
{
if(q&1)
{
ret*=p;ret%=mod;
}
q>>=1;
p=p*p%mod;
}
return ret;
}
int main()
{
for(ll i=1;i<=40;i++)
{
for(ll j=0;j<=i;j++)tab[i][j]+=tab[i-1][j],tab[i][j]=(tab[i][j]%mod+mod)%mod;
}
ll n,m;
while(~scanf("%lld%lld",&n,&m))
{
ll ans=0;
for(ll j=0;j<=n;j++)ans+=(tab[n][j]*pow(m,j))%mod,ans%=mod;
printf("%lld\n",ans);
}
}
初赛五 腾讯的新游戏
勇士挑战恶魔,恶魔有n队,每队mi个
q次换防,i队前几个与j队前几个互换
1、链表实现换防
2、逐一计算攻击每队的所需防御力
3、按照防御力从小到大排序,贪心即可
初赛六
1、树形dp f[u][color]
2、再次考到容斥原理
3、dfs的复杂度是O(n)的!dfs的复杂度是O(n)!的dfs的复杂度是O(n)的!当时没想到这一点,不敢枚举最大最小值
#include<cstdio>
#include<cstring>
#include<vector>
#define ll long long
#define D(x) ((x)>-(x)?(x):-(x))
using namespace std;
const ll inf=1e15;
ll dat[110][2],n;
ll a[110],b[110],top;
namespace G
{
vector<ll>V[110];
ll f[110][2];
void dfs(ll u,ll fa)
{
ll len=V[u].size();
for(ll i=0;i<len;i++)
{
ll v=V[u][i];
if(v==fa)continue;
dfs(v,u);
}
ll node; if(u==1)node=len;else node=len-1;
if(node==0)
{
f[u][0]=dat[u][0];f[u][1]=dat[u][1];return;
}
if(node==1)
{
ll v;for(ll i=0;i<len;i++)if(V[u][i]!=fa)v=V[u][i];
f[u][0]=max(dat[u][0]+f[v][0]-(D(dat[u][0]-dat[v][0])+999)/1000*666*u,
dat[u][0]+f[v][1]-(D(dat[u][0]-dat[v][1])+999)/1000*666*u);
f[u][1]=max(dat[u][1]+f[v][0]-(D(dat[u][1]-dat[v][0])+999)/1000*666*u,
dat[u][1]+f[v][1]-(D(dat[u][1]-dat[v][1])+999)/1000*666*u);
return;
}
f[u][0]=f[u][1]=-inf;
ll mx,mi,mxf,mif;
for(ll i=0;i<len;i++)//第一个点
{
ll t1=V[u][i];if(t1==fa)continue;
for(ll c1=0;c1<2;c1++)
{
for(ll j=i+1;j<len;j++)//第二个点
{
ll t2=V[u][j];if(t2==fa)continue;
for(ll c2=0;c2<2;c2++)
{
for(ll c0=0;c0<2;c0++)//u点
{
if(dat[t1][c1]>dat[t2][c2])
mx=dat[t1][c1],mxf=f[t1][c1],mi=dat[t2][c2],mif=f[t2][c2];
else
mi=dat[t1][c1],mif=f[t1][c1],mx=dat[t2][c2],mxf=f[t2][c2];
mx=max(mx,dat[u][c0]);
mi=min(mi,dat[u][c0]);
ll ret=dat[u][c0]+mxf+mif-(mx-mi+999)/1000*666*u;
ll flag=1;
for(ll l=0;l<len;l++)if(l-i&&l-j)//其他点
{
ll t3=V[u][l];if(t3==fa)continue;
ll tmp=-inf;
if(mi<=dat[t3][0]&&dat[t3][0]<=mx)tmp=max(tmp,f[t3][0]);
if(mi<=dat[t3][1]&&dat[t3][1]<=mx)tmp=max(tmp,f[t3][1]);
if(tmp==-inf)
{
flag=0;break;
}
else ret+=tmp;
}
if(flag)f[u][c0]=max(f[u][c0],ret);
}
}
}
}
}
}
void Go()
{
while(~scanf("%lld",&n))
{
for(ll i=1;i<=n;i++)scanf("%lld%lld",&dat[i][0],&dat[i][1]),V[i].clear();
for(ll i=1;i<n;i++)
{
ll x,y;
scanf("%lld%lld",&x,&y);
V[x].push_back(y);
V[y].push_back(x);
}
dfs(1,-1);
printf("%lld\n",max(f[1][0],f[1][1]));
}
}
}
int main()
{
G::Go();
}