Problem A.(…)
#include<bits/stdc++.h>
using namespace std;
void dfs(int x,string a,string b,string c){
if(x==a.length()){
if(b==c){puts("YES");exit(0);}
else return ;
}
if(a[x]=='A')dfs(x+1,a,b,c);
dfs(x+1,a,b,c+a[x]);
}
int main(){
string s,t="AKIHABARA";
cin>>s;
dfs(0,t,s,"");
puts("NO");
}
Problem B.(…)
#include<bits/stdc++.h>
using namespace std;
char s[100100];
int a[100];
int main(){
scanf("%s",s);
for(int i=0;s[i];i++)
a[s[i]-'a']++;
int c=min(a[0],min(a[1],a[2]));
for(int i=0;i<3;++i)
if((a[i]-=c)>=2)return puts("NO"),0;
puts("YES");
}
Problem C.
可以发现0~12的数每种只能变成d或24-d,枚举变成哪个即可,复杂度O(212∗122)
#include<bits/stdc++.h>
using namespace std;
int n,a[20],c[100],tp,ans;
int chk(){
int ret=10007;
for(int i=0;i<=tp;++i)
for(int j=i+1;j<=tp;++j)
ret=min(ret,min(abs(c[i]-c[j]),24-abs(c[i]-c[j])));
return ret;
}
void dfs(int x){
if(x==13){
ans=max(ans,chk());
return ;
}
if(a[x]==2)c[++tp]=x,c[++tp]=24-x,dfs(x+1),tp-=2;
else if(a[x]==1)c[++tp]=x,dfs(x+1),c[tp]=24-x,dfs(x+1),tp--;
else dfs(x+1);
}
int main(){
scanf("%d",&n);
for(int i=1,x;i<=n;++i)
scanf("%d",&x),a[x]++;
for(int i=0;i<=12;++i)
if(a[i]>2)return puts("0"),0;
dfs(0);
printf("%d",ans);
}
Problem D.
可以证明,被选的人的Hi+Pi一定是递增的。
证明如下:假设有两个人a,b,那么a在b前面的条件就是min(Ha,Hb−Pa)≥min(Hb,Ha−Pb),即Ha+Pa≤Hb+Pb
于是可以拿一个dp(i,j)表示前i个人已经选了
(直接贪心是错的,因为不能将全部人选完)
#include<bits/stdc++.h>
#define maxn 5010
using namespace std;
struct people{
int h,p;
void scan(){scanf("%d%d",&h,&p);}
int operator<(const people& a)const{
return p+h<a.p+a.h;
}
}A[maxn];
int n,ans;
long long dp[maxn];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)A[i].scan();
sort(A+1,A+n+1);
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
for(int i=1;i<=n;++i)
for(int j=i;j>=0;--j)if(dp[j]<=A[i].h)
dp[j+1]=min(dp[j+1],dp[j]+A[i].p);
for(int i=n;i>=1;--i)if(dp[i]!=0x3f3f3f3f3f3f3f3fll)return printf("%d",i),0;
}
Problem E.
首先将整个序列差分,问题转化为“将一些位置+1/-1,使得(i,n+2−i)和为26的倍数”
对于每种操作连边,再连上(i,n+2−i),则问题等价于每个联通块的和为26的倍数。因为无论怎么+1/-1,每个联通块的和是不变的。
用并查集维护就可以了。
#include<bits/stdc++.h>
#define maxn 1000100
using namespace std;
int n,sum[maxn*2],tp,m,a[maxn],f[maxn];
char s[maxn];
int g(int x){
return x<0?x+26:x;
}
int find(int x){
return x==f[x]?x:f[x]=find(f[x]);
}
int main(){
scanf("%s%d",s+1,&n);
int m=strlen(s+1);
for(int i=1;i<=m;++i)s[i]-='a',f[i]=i;
f[m+1]=m+1;
for(int i=m+1;i>=1;--i)s[i]=g(s[i]-s[i-1]);
for(int i=1,l,r;i<=n;++i)
scanf("%d%d",&l,&r),f[find(l)]=find(r+1);
for(int i=1;i<=m-i+2;++i)f[find(i)]=find(m+2-i);
for(int i=1;i<=m+1;++i)sum[find(i)]+=s[i];
for(int i=1;i<=m+1;++i)if(i==find(i)&&sum[i]%26)return puts("NO"),0;
puts("YES");
}
Problem F.
这个题。。。看题先打表。。。然后发现符合条件的N,K特别少。
可以观察一些小数据:
7 3
1 2 3
1 4 5
1 6 7
2 4 6
2 5 7
3 4 7
3 5 6
可以发现,它的构造方式为:
1.前K行开头为1,后面依次分配剩余元素。这只能在
2.后面[2,K]开头时,将后面分为K−1块,每块每次等距地选,比如上图就是:
1:[2,3],[4,5],[6,7]
块:[4,5][6,7]
2:{1,1}{2,2}(相距0)
3:{1,2}{2,1}(相距1)
这只有在K−1为质数时才能符合题目条件。
#include<bits/stdc++.h>
using namespace std;
int n,k,a[1000][100];
int main(){
k=42,n=k*(k-1)+1;
printf("%d %d",n,k);
for(int i=1;i<=k;++i){
printf("\n1 ");
for(int j=(i-1)*(k-1)+2,p=1;p<=k-1;p++,j++)
printf("%d ",j),a[i][p-1]=j;
}
for(int i=0;i<k-1;++i){
for(int j=0;j<k-1;++j){
printf("\n%d ",i+2);
for(int l=2,ptr=j;l<=k;l++,ptr=(ptr+i)%(k-1))
printf("%d ",a[l][ptr]);
}
}
}
Problem G.
考虑对于一个固定的序列a如何求答案。显然,可以用一个
dp(i)={(ai+∑nj=i+1dp(j))/i0ai≤iai>i
相当于是每次从左往右一遇到能选的就选,然后开始下一轮循环。
最终答案是∑ni=1ai−∑ni=1dp(i)
考虑两边分别统计,每个元素在[0,K]之间,所以所有ai的和就是:
(K+1)n−1×K(K+1)2×n=nK2×(K+1)n
如何求得所有情况dp的和呢?考虑用f(i,j)表示[i,n]的元素dp值的和为j的方案数。
每次枚举该元素是
根据上面的转移很容易得到f(i,j)的转移。
可以发现j不是很大,所以复杂度很小。
#include<bits/stdc++.h>
#define mod 1000000007
using namespace std;
int dp[110][11000],n,k,sum;
int qpow(int a,int b){
int ans=1,tmp=a;
for(;b;b>>=1,tmp=1ll*tmp*tmp%mod)
if(b&1)ans=1ll*ans*tmp%mod;
return ans;
}
int main(){
scanf("%d%d",&n,&k);
dp[n+1][0]=1,sum=0;
for(int i=n;i>=1;sum+=(k+sum)/i,--i)
for(int j=0;j<=sum;++j)if(dp[i+1][j])
for(int a=0;a<=k;++a)
if(a>i)dp[i][j]=(dp[i][j]+dp[i+1][j])%mod;
else dp[i][j+(a+j)/i]=(dp[i][j+(a+j)/i]+dp[i+1][j])%mod;
int ans=0;
for(int i=0;i<=sum;++i)
ans=(ans+1ll*dp[1][i]*i)%mod;
printf("%d",(1ll*k*qpow(k+1,n)%mod*n%mod*(mod+1)/2%mod-ans+mod)%mod);
}
Problem H.
这题,崩坏的条件是上下和左右都要满足至少一格崩坏或不存在。
考虑每个子矩形,他与整块大陆脱离的最小代价设为
***### ###***
***### ###***
***### ###***
###111 111###
###111 111###
###111 111###
(***是正在考虑的矩形,###是要计算的区域,111是无关紧要的区域)
那么答案就是下图#位置的冰山数+dp值(4个方向都枚举):
###***
###***
##P***
******
******
******
复杂度O(n6)(???
#include<bits/stdc++.h>
using namespace std;
int dp[50][50][50][50],a[50][50],n,m,px,py,ret=0x3f3f3f3f;
char s[50][50];
int g(int x1,int y1,int x2,int y2){
x1--,x2--,y1--,y2--;
return a[x2][y2]-a[x2][y1]-a[x1][y2]+a[x1][y1];
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)scanf("%s",s[i]+1);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+(s[i][j]=='#');
if(s[i][j]=='P')px=i,py=j;
}
memset(dp,0x3f,sizeof(dp)),dp[1][1][n+1][m+1]=0;
for(int w=n;w>=1;--w)
for(int h=m;h>=1;--h)
for(int i=1;i+w<=n+1;i++)if(i<=px&&px<i+w)
for(int j=1;j+h<=m+1;j++)if(j<=py&&py<j+h){
int& ans=dp[i][j][i+w][j+h];
for(int _w=w;_w<=n+1;++_w)
for(int _h=h;_h<=m+1;++_h)if(_w!=w||_h!=h){
ans=min(ans,dp[i][j][i+_w][j+_h]+g(i+w,j,i+_w,j+h)+g(i,j+h,i+w,j+_h));
if(j+h-_h>=1)ans=min(ans,dp[i][j+h-_h][i+_w][j+h]+g(i,j+h-_h,i+w,j)+g(i+w,j,i+_w,j+h));
if(i+w-_w>=1)ans=min(ans,dp[i+w-_w][j][i+w][j+_h]+g(i+w-_w,j,i,j+h)+g(i,j+h,i+w,j+_h));
if(j+h-_h>=1&&i+w-_w>=1)ans=min(ans,dp[i+w-_w][j+h-_h][i+w][j+h]+g(i,j+h-_h,i+w,j)+g(i+w-_w,j,i,j+h));
}
if(i<=px&&px<i+w&&j<=py&&py<j+h){
ret=min(ret,ans+g(i,j,px+1,py+1));
ret=min(ret,ans+g(px,py,i+w,j+h));
ret=min(ret,ans+g(i,py,px+1,j+h));
ret=min(ret,ans+g(px,j,i+w,py+1));
}
}
printf("%d",ret);
}
Problem I.
题解证明了一个结论:设最后排名为i的队为
那么ai必定会与ai|2j满足且ai<ai|2j
那么i向
枚举[0,2n−1],到一个区间就把他加入堆。每次取出R最小的将
这样得到了一个合法的排名。可以将二进制位反转得到原序列。大概就跟FFT那个位运算变换一样。
0 1 2 3 4 5 6 7->0 4 2 6 1 5 3 7
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> par;
int a[1<<18],n,m,bh[1<<18];
par b[1<<18];
vector<par>v[1<<18];
priority_queue<par,vector<par>,greater<par> >pq;
int main(){
scanf("%d",&n),m=1<<n;
for(int i=0;i<m;++i){
bh[i]=(bh[i>>1]>>1)|((i&1)<<(n-1));
scanf("%d",&a[i]),a[i]--;
if(a[i]>=0)b[i]=par(a[i],a[i]);
else b[i]=par(0,m-1);
}
for(int i=m-1;i>=0;--i)
for(int j=0;j<n;++j)if(~i>>j&1)
b[i].second=min(b[i].second,b[i^(1<<j)].second);
for(int i=0;i<m;++i)
for(int j=0;j<n;++j)if(~i>>j&1)
b[i^(1<<j)].first=max(b[i^(1<<j)].first,b[i].first);
for(int i=0;i<m;++i)
if(b[i].first>b[i].second)return puts("NO"),0;
else v[b[i].first].push_back(par(b[i].second,i));
for(int i=0;i<m;++i){
for(int j=0;j<v[i].size();++j)
pq.push(v[i][j]);
if(pq.empty())continue;
if(pq.top().first<i)return puts("NO"),0;
else a[pq.top().second]=i,pq.pop();
}
puts("YES");
for(int i=0;i<m;++i)
printf("%d ",a[bh[i]]+1);
}
Problem J.
这题。。。可以看题解,题解有一个奇妙的算法
其实可以点分治,每次只考虑经过某个点的边。这样每个子树内的点只连其他子树中最小的vali+depi
只有O(nlogn)条边,时间复杂度O(nlog2n)
#include<bits/stdc++.h>
#define maxn 200100
using namespace std;
typedef long long ll;
typedef pair<ll,int> par;
struct edge{
int r,nxt,w;
}e[maxn<<1];
struct data{
int u,v;
ll w;
int operator<(const data& d)const{return w<d.w;}
}d[maxn*100];
int f[maxn],head[maxn],tp,esz,num,mn,rt,vis[maxn],sz[maxn],A[maxn],a[maxn],n;
void addedge(int u,int v,int w){
e[++esz].r=v;e[esz].nxt=head[u];head[u]=esz;e[esz].w=w;
e[++esz].r=u;e[esz].nxt=head[v];head[v]=esz;e[esz].w=w;
}
void pre(int u,int f){
num++;
for(int t=head[u];t;t=e[t].nxt)if(!vis[e[t].r]&&e[t].r!=f)
pre(e[t].r,u);
}
int findrt(int u,int f){
int sz=1,s=0,mxsize=0;
for(int t=head[u];t;t=e[t].nxt)if(!vis[e[t].r]&&e[t].r!=f){
s=findrt(e[t].r,u),sz+=s;
if(mxsize<s)mxsize=s;
}
if(mxsize<num-sz)mxsize=num-sz;
if(mxsize<mn)mn=mxsize,rt=u;
return sz;
}
par cal(int u,int f,ll d){
par ret=par(a[u]+d,u);
for(int t=head[u];t;t=e[t].nxt)if(!vis[e[t].r]&&e[t].r!=f)
ret=min(ret,cal(e[t].r,u,d+e[t].w));
return ret;
}
void join(int u,int f,ll d,par p){
::d[++tp]=data{u,p.second,p.first+a[u]+d};
for(int t=head[u];t;t=e[t].nxt)if(!vis[e[t].r]&&e[t].r!=f)
join(e[t].r,u,d+e[t].w,p);
}
void sol(int u){
num=0,mn=1<<30,rt=u;
pre(u,0),findrt(u,0);
vis[u=rt]=1;
int ptr=0;
par mn(a[u],u);
for(int t=head[u];t;t=e[t].nxt)if(!vis[e[t].r])A[++ptr]=t;
for(int i=1,t;i<=ptr;++i){
t=A[i];
join(e[t].r,u,e[t].w,mn);
mn=min(mn,cal(e[t].r,u,e[t].w));
}
mn=par(a[u],u);
for(int i=ptr,t;i>=1;--i){
t=A[i];
join(e[t].r,u,e[t].w,mn);
mn=min(mn,cal(e[t].r,u,e[t].w));
}
::d[++tp]=data{u,mn.second,mn.first+a[u]};
for(int t=head[u];t;t=e[t].nxt)if(!vis[e[t].r])sol(e[t].r);
}
int find(int x){
return x==f[x]?x:f[x]=find(f[x]);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
for(int i=2,u,v,w;i<=n;++i){
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
}
sol(1);
sort(d+1,d+tp+1);
long long sum=0;
for(int i=1;i<=n;++i)f[i]=i;
for(int i=1;i<=tp;++i){
int u=find(d[i].u),v=find(d[i].v);
if(u!=v)sum+=d[i].w,f[u]=v;
}
printf("%lld",sum);
}