1001
思路:
问题数m是不超过10的,所以我们对问题数 m 进行状态压缩,每个状态可以得到一些问题的集合,这些问题集合是属于这 m 个问题集合中的,并且不会重复的,所以我们根据每个状态可以得到 n 个人在这个状态下的回答情况,因为每个问题的答案只有 ‘A’ 或者 ‘B’ ,所以我将这 n 个人在这个状态下的回答情况变成一个二进制数(‘A’ 看成 0 ‘B’ 看成 1),这样就可以很简单的得到这 n 个人中有多少对不同的答案,再和 k 进行比较计数即可。
代码:
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1000+10;
char ch[maxn][20];
int kaven[maxn];
int M[1<<11];
int main(){
int T,C=0;
scanf("%d",&T);
while(T--){
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
getchar();
for(int i=1;i<=n;i++) scanf("%s",ch[i]);
if(n*(n-1)/2<k){
printf("Case #%d: 0\n",++C);
continue;
}
int cnt=0;
for(int i=1;i<(1<<m);i++){
for(int j=1;j<=n;j++){
int tot=0;
for(int t=0;t<m;t++){
if(i>>t&1 && ch[j][t]=='B'){
tot+=(1<<t);
}
}
kaven[j]=tot;
M[tot]=0;
}
int num=0;
for(int j=1;j<=n;j++){
int cnt= ++M[kaven[j]];
//printf("%d %d\n",kaven[j],M[kaven[j]]);
num+=(j-cnt);
}
if(num>=k) cnt++;
}
printf("Case #%d: %d\n",++C,cnt);
}
}
1002
思路:
我是用莫队写的,可能写这种区间问题我用莫队写习惯了,莫队写这个题也很简单,当然其他的想法也可以,比如前缀和啥的,我就不重复写了,下面是莫队思路的代码
代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=100000+100;
char ch[maxn];
struct Mo{
int l,r;
int id;
}mo[maxn];
int R[maxn];
int Ans[maxn];
int num[30];
inline bool cmp(Mo a,Mo b){
return R[a.l]==R[b.l]?a.r<b.r:R[a.l]<R[b.l];
}
inline void kaven(int ind,int v){
int tmp=(int)(ch[ind]-'A');
num[tmp]+=v;
}
int main(){
int T,C=0;
scanf("%d",&T);
while(T--){
int n,m;
scanf("%d%d",&n,&m);
getchar();
scanf("%s",ch+1);
int l=1,r=0;
int size=sqrt(1.0*n);
for(int i=0;i<30;i++) num[i]=0;
for(int i=1;i<=n;i++) R[i]=i/size;
for(int i=1;i<=m;i++) scanf("%d%d",&mo[i].l,&mo[i].r),mo[i].id=i;
sort(mo+1,mo+1+m,cmp);
for(int i=1;i<=m;i++){
while(l>mo[i].l) kaven(l-1,1),l--;
while(r<mo[i].r) kaven(r+1,1),r++;
while(l<mo[i].l) kaven(l,-1),l++;
while(r>mo[i].r) kaven(r,-1),r--;
int key;
for(key=0;key<=25;key++) if(num[key]) break;
Ans[mo[i].id]=num[key];
}
printf("Case #%d:\n",++C);
for(int i=1;i<=m;i++) printf("%d\n",Ans[i]);
}
}
1003
思路:
条件 x[i] + y[j] <= a[i][j]
求 x[1]+x[2]+…+x[n]+y[1]+y[2]+…+y[n]的最大值
我们可以把 x[] 和 y[] 进行两两组合
比如:n=2的情况
第一种:x[1] + y[1],x[2] + y[2] 即 a[1][1] ,a[2][2]
第二种:x[1] + y[2],x[2] + y[1] 即 a[1][2] ,a[2][1]
所以答案就是 min( a[1][1] +a[2][2], a[1][2] +a[2][1] )
所以答案就是 在矩阵不同行不同列中取 n 个数的最小值,用 KM 即可
怪我 KM 的板子质量太差了,比赛的时候无限 T
下面代码是我新的 KM 板子
代码:
DFS实现
#include<cstdio>
#include<algorithm>
using namespace std;
#define MEM(a,b,start,end) for(int ii=start;ii<=end;ii++) a[ii]=b
#define max(a,b) a>b?a:b
#define min(a,b) a<b?a:b
const int INF=0x3f3f3f3f;
const int maxn=250;
int xy[maxn][maxn],match[maxn],slack[maxn],valx[maxn],valy[maxn];
bool visx[maxn],visy[maxn];
int n;
inline bool DFS(int u,int key=1){
if(visx[u]) return false;
visx[u]=true;
for(int i=1;i<=n;i++){
if(!visy[i]){
int tmp=valx[u]+valy[i]-xy[u][i];
if(tmp==0){
visy[i]=true;
if(match[i]==-1 || DFS(match[i],key)){
if(key) match[i]=u;
return true;
}
}
else slack[i]=min(slack[i],tmp);
}
}
return false;
}
long long KM(){
for(int i=1;i<=n;i++){
MEM(visx,false,1,n);
MEM(visy,false,1,n);
MEM(slack,INF,1,n);
if(DFS(i)) continue;
bool isok=true;
while(isok){
int tmp=INF;
for(int j=1;j<=n;j++){
if(!visy[j] && tmp>slack[j]) tmp=slack[j];
}
for(int j=1;j<=n;j++){
if(visx[j]) valx[j]-=tmp;
if(visy[j]) valy[j]+=tmp;
else slack[j]-=tmp;
}
for(int j=1;j<=n;j++){
if(!visy[j] && slack[j]==0){
visy[j]=true;
if(match[j]==-1 || DFS(match[j],0)){
isok=false;
break;
}
}
}
}
MEM(visx,false,1,n);
MEM(visy,false,1,n);
DFS(i);
}
long long res=0;
for(int i=1;i<=n;i++) res+=xy[match[i]][i];
return res;
}
int main(){
int T,C=0;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++){
valx[i]=-INF;
valy[i]=0;
match[i]=-1;
for(int j=1;j<=n;j++) scanf("%d",&xy[i][j]),xy[i][j]*=-1,valx[i]=max(valx[i],xy[i][j]);
}
printf("Case #%d: %lld\n",++C,-KM());
}
}
BFS实现
#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MEM(a,b,start,end) for(int ii=start;ii<=end;ii++) a[ii]=b
#define max(a,b) a>b?a:b
#define min(a,b) a<b?a:b
const int INF=0x3f3f3f3f;
const int maxn=250;
int xy[maxn][maxn],valx[maxn],valy[maxn],slack[maxn],l[maxn],r[maxn],pre[maxn];
bool visx[maxn],visy[maxn];
int n;
void BFS(int u){
queue<int> Q;
Q.push(u);
visx[u]=true;
while(1){
while(!Q.empty()){
int x=Q.front();
Q.pop();
for(int y=1;y<=n;y++){
int tmp=valx[x]+valy[y]-xy[x][y];
if(!visy[y] && tmp<=slack[y]){
pre[y]=x;
if(!tmp){
if(!l[y]){
for(;y;l[y]=pre[y],swap(y,r[pre[y]]));
return;
}
else{
visx[l[y]]=visy[y]=true;
Q.push(l[y]);
}
}
else{
slack[y]=tmp;
}
}
}
}
int tmp=INF;
int node=u;
for(int i=1;i<=n;i++){
if(!visy[i] && slack[i]<=tmp){
node=i,tmp=slack[i];
}
}
for(int i=1;i<=n;i++){
if(visx[i]) valx[i]-=tmp;
if(visy[i]) valy[i]+=tmp;
else slack[i]-=tmp;
}
if(!l[node]){
for(;node;l[node]=pre[node],swap(node,r[pre[node]]));
return;
}
else{
visx[l[node]]=visy[node]=true;
Q.push(l[node]);
}
}
}
void KM(){
for(int i=1;i<=n;i++){
MEM(visx,false,1,n);
MEM(visy,false,1,n);
MEM(slack,INF,1,n);
BFS(i);
}
}
int main(){
int T,C=0;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++){
valx[i]=-INF;
valy[i]=0;
l[i]=r[i]=pre[i]=0;
for(int j=1;j<=n;j++) scanf("%d",&xy[i][j]),xy[i][j]*=-1,valx[i]=max(valx[i],xy[i][j]);
}
KM();
long long res=0;
for (int i=1;i<=n;i++) res+=(-valx[i]-valy[i]);
printf("Case #%d: %I64d\n",++C,res);
}
}
1005
思路:
因为数据随机,所以数据中每种排列的最长上升子序列长度的期望是 n^(0.5)(题解说的,我也不知道),所以比赛的时候,我想了一个动态规划的解法,dp[ind][len]=sum(dp[res][len-1]) (ind>res,id[res] < id[ind] ,id[i] 表示 i 这个数在排列中的位置,也就是 ind 在排列中,前面比它小的数- res ) ,这个 sum(dp[res][len-1]) 用树状数组来维护就可以了,当时感觉复杂度还是很高,又想了想,当长度为 len 的最长上升子序列不存在时,长度为 len+1 的最长上升子序列肯定不存在,所以就加了个判断条件,因为数据随机,我觉得应该可以A,没想到可以1A,具体看代码吧,因为开 dp[][] 这个二位数组感觉没什么用,所以你可以把下面代码中的tree[][](树状数组)当作是dp[][]。。。
代码:
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=10000+10;
const int mod=1e9+7;
int tree[maxn][maxn],ans[maxn];
int n;
inline int lowbit(int x){
return x&(-x);
}
inline void Add(int ind,int len,int v){
while(ind<=n){
tree[ind][len]=(tree[ind][len]+v)%mod;
ind+=lowbit(ind);
}
}
inline int getSum(int ind,int len){
int sum=0;
while(ind>0){
sum=(sum+tree[ind][len])%mod;
ind-=lowbit(ind);
}
return sum;
}
int main(){
int T,C=0;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&ans[i]);
for(int i=1;i<=n;i++){
Add(ans[i],1,1);
for(int j=2;j<=i;j++){
int val=getSum(ans[i]-1,j-1);
if(val==0) break;
Add(ans[i],j,val);
}
}
int i;
int val=1;
printf("Case #%d: ",++C);
for(i=1;i<=n;i++){
if(val) {
val=getSum(n,i);
for(int j=1;j<=n;j++) tree[j][i]=0;
}
else val=0;
printf("%d%c",val,i==n?'\n':' ');
}
}
}
1006
思路:
复制一下Q神的题解:
满足条件的 k 条边必然包含一个由红色边和绿色边构成的最小生成树或者一个由蓝色边和绿色边构成的最小生成树,不难证明一个图中所有最小生成树的边权集合是相同的,于是可以对两种情况分别求出一个最小生成树,再按照边权从小到大的顺序加入不在最小生成树上的边。
代码:
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
#define MEM(a,b) memset(a,b,sizeof(a))
const int maxn=110;
struct Edge{
int from,to,w;
char type;
int id;
Edge(int from_,int to_,int w_,char type_,int id_):from(from_),to(to_),w(w_),type(type_),id(id_){}
};
vector<Edge>RG,BG;
int fa[maxn][2];
bool vis[maxn<<2][2];
int n,m;
inline bool cmp(Edge a,Edge b){
return a.w<b.w;
}
inline int Find(int x,int reg){
if(x!=fa[x][reg]) fa[x][reg]=Find(fa[x][reg],reg);
return fa[x][reg];
}
inline void Union(int x,int y,int reg){
int X=Find(x,reg);
int Y=Find(y,reg);
if(X<Y) fa[Y][reg]=X;
else fa[X][reg]=Y;
}
inline pair<int,int> kaven(vector<Edge> G,int reg,char type1,char type2){
int mst=0; //最小生成树
int cnt=0; //边数
for(int i=0;i<m*2 && cnt<n-1;i++){
Edge e=G[i];
int from=e.from,to=e.to,w=e.w,type=e.type;
if(type!=type1 && type!=type2) continue;
if(Find(from,reg)!=Find(to,reg)){
Union(from,to,reg);
mst+=w;cnt++;
vis[e.id][reg]=vis[e.id^1][reg]=true;
//printf("---%d %d---\n",from,to);
}
}
//printf("%d %d\n",mst,cnt);
if(cnt==n-1) return make_pair(mst,cnt);
else return make_pair(1e9,cnt);
}
int main(){
int T,C=0;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
RG.clear();
BG.clear();
for(int i=1;i<=m;i++){
int from,to,w;
char type;
scanf("%d%d%d %c",&from,&to,&w,&type);
//if(type<'A' || type>'Z') type=type-'a'+'A';
RG.push_back(Edge(from,to,w,type,2*i));
RG.push_back(Edge(to,from,w,type,2*i+1));
BG.push_back(Edge(from,to,w,type,2*i));
BG.push_back(Edge(to,from,w,type,2*i+1));
//printf("%d %d %d %c\n",from,to,w,type);
}
sort(RG.begin(),RG.end(),cmp);
sort(BG.begin(),BG.end(),cmp);
MEM(vis,false);
for(int i=1;i<=n;i++) fa[i][0]=fa[i][1]=i;
pair<int,int> rg=kaven(RG,0,'R','G');
pair<int,int> bg=kaven(BG,1,'B','G');
printf("Case #%d:\n",++C);
if(rg.first==1e9 && bg.first==1e9){
for(int i=1;i<=m;i++) printf("-1\n");
}
else{
int minedge=min(rg.second,bg.second);
for(int i=1;i<minedge;i++) printf("-1\n");
for(int i=minedge;i<=m;i++){
int minval=1e9;
if(rg.first!=1e9 && rg.second<i){
for(int j=0;j<m*2;j++){
Edge e=RG[j];
int w=e.w;
if(!vis[e.id][0]){
rg.first+=w;
rg.second++;
vis[e.id][0]=vis[e.id^1][0]=true;
break;
}
}
}
if(bg.first!=1e9 && bg.second<i){
for(int j=0;j<m*2;j++){
Edge e=BG[j];
int w=e.w;
if(!vis[e.id][1]){
bg.first+=w;
bg.second++;
vis[e.id][1]=vis[e.id^1][1]=true;
break;
}
}
}
if(rg.second==i) minval=min(minval,rg.first);
if(bg.second==i) minval=min(minval,bg.first);
printf("%d\n",minval==1e9?-1:minval);
}
}
}
}