1001
题意:给n份问卷,每个问卷m道题,每题只有A,B两种选项,问存在多少个问题集合,使得只保留这些问题后至少k对卷子不同。
思路:状压,最大只有(1<<10),用二进制表示选择了哪些题,转为数字,统计这个数字出现的次数 num , num * ( n - num ) 就是这个集合对 不同卷子对数 贡献的2倍,除以2(去重),跟k比较。
Code:
#include <bits/stdc++.h>
using namespace std;
const int AX = 1024;
int a[AX];
char s[AX][20];
int b[AX][20];
unordered_map<int,int>mp;
int main(){
int T;
scanf("%d",&T);
int n , m , k ;
int Case = 0 ;
while( T-- ){
int res = 0 ;
scanf("%d%d%d",&n,&m,&k);
for( int i = 0 ; i < n ; i++ ){
scanf("%s",s[i]);
}
int MAX = ( 1 << m );
for( int i = 1 ; i < MAX ; i++ ){
int tot = 0 ;
for( int j = 0 ; j < m ; j++ ){
if( i & ( 1 << j ) ){
for( int kk = 0 ; kk < n ; kk++ ){
b[kk][tot] = s[kk][j] - 'A';
}
tot ++;
}
}
for( int j = 0 ; j < n ; j++ ){
a[j] = 0 ;
for( int kk = 0 ; kk < tot ; kk++ ){
a[j] <<= 1 ;
a[j] |= b[j][kk];
}
}
mp.clear();
for( int kk = 0 ; kk < n ; kk++ ){
mp[a[kk]] ++;
}
int tmp = 0;
for( auto kk : mp ){
tmp += ( n - kk.second ) * kk.second;
}
if( tmp / 2 >= k ) res ++;
}
printf("Case #%d: %d\n", ++Case, res);
}
return 0 ;
}
1002
思路:前缀和维护即可。
Code:
#include <bits/stdc++.h>
using namespace std;
const int AX = 1e5+66;
int sum[AX][30];
char s[AX];
int main(){
int T;
scanf("%d",&T);
int Case = 0;
while( T-- ){
memset( sum , 0 , sizeof(sum) );
int n , q ;
scanf("%d%d",&n,&q);
scanf("%s",s+1);
int l , r ;
for( int i = 1 ; i <= n ; i++ ){
int id = s[i] - 'A';
for( int j = 0 ; j < 26 ; j++ ){
if( id == j ) sum[i][id] = sum[i-1][id] + 1 ;
else sum[i][j] = sum[i-1][j] ;
}
}
printf("Case #%d:\n",++Case);
while( q -- ){
scanf("%d%d",&l,&r);
for( int i = 0 ; i < 26 ; i++ ){
if( sum[r][i] - sum[l-1][i] ){
printf("%d\n",sum[r][i] - sum[l-1][i]);
break;
}
}
}
}
return 0 ;
}
1003
题意:看作是二分图,那么本题就是求顶标和的最大值,也就是最小权匹配
思路:bfs实现的KM算法求最小权匹配,先取负值,求出最大权匹配后,取反即可。
Code:
#include <bits/stdc++.h>
#pragma comment(linker, “/STACK:1024000000,1024000000”)
#define INF 1e9
#define LL long long
using namespace std;
const int AX = 3e2+6;
LL w[AX][AX];
LL lx[AX] , ly[AX];
int linker[AX];
LL slack[AX];
int n ;
bool visy[AX];
int pre[AX];
void bfs( int k ){
int x , y = 0 , yy = 0 , delta;
memset( pre , 0 , sizeof(pre) );
for( int i = 1 ; i <= n ; i++ ) slack[i] = INF;
linker[y] = k;
while(1){
x = linker[y]; delta = INF; visy[y] = true;
for( int i = 1 ; i <= n ;i++ ){
if( !visy[i] ){
if( slack[i] > lx[x] + ly[i] - w[x][i] ){
slack[i] = lx[x] + ly[i] - w[x][i];
pre[i] = y;
}
if( slack[i] < delta ) delta = slack[i] , yy = i ;
}
}
for( int i = 0 ; i <= n ; i++ ){
if( visy[i] ) lx[linker[i]] -= delta , ly[i] += delta;
else slack[i] -= delta;
}
y = yy ;
if( linker[y] == -1 ) break;
}
while( y ) linker[y] = linker[pre[y]] , y = pre[y];
}
void KM(){
memset( lx , 0 ,sizeof(lx) );
memset( ly , 0 ,sizeof(ly) );
memset( linker , -1, sizeof(linker) );
for( int i = 1 ; i <= n ; i++ ){
memset( visy , false , sizeof(visy) );
bfs(i);
}
}
int main(){
int T;
scanf("%d",&T);
int Case = 0 ;
LL x ;
while( T-- ){
scanf("%d",&n);
for( int i = 1 ; i <= n ; i++ ){
for( int j = 1 ; j <= n ; j++ ){
scanf("%lld",&x);
w[i][j] = 0 - x ;
}
}
KM();
LL res = 0 ;
for( int i = 1 ; i <= n ; i++ ){
if( linker[i] != -1 ){
res += w[linker[i]][i] ;
}
}
printf("Case #%d: %lld\n",++Case,0-res);
}
return 0 ;
}
1005
题意:1-n的随机排列,问长度为k的上升子序列数分别为多少。
思路:首先会容易想到一个O(n^3) 的dp:
dp[ i ][ j ] = Σ ( 1 <= k <= j ) dp[i-1] [ k ] ( a[k] < a[j] )
dp[i][j] 表示以a[j]结尾长度为i的上升序列个数。
但是题目的n是1e4数量级的,会T.
然后我想用二维树状数组对于每个j建立一个树状数组,维护dp[i][j]前缀和又MLE了。
请教了别人后发现可以在加一个滚动数组减少树状数组维护的工作量,
最后用一维树状数组维护每次长度以a[j]结尾的前缀和,然后加个dp[][j]滚动数组记录以a[j]结尾的长度 i 的序列个数.
复杂度就转化成n*n*logn(其实应该还好点,听大佬说是sqrt(n)*n*logn)。
我多写个memset就TLE了。。以后不该写的代码不要写。。
Code:
#include <bits/stdc++.h>
#pragma comment(linker, “/STACK:1024000000,1024000000”)
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int AX = 1e4;
const int MOD = 1e9+7;
int a[AX];
int c[AX];
int dp[2][AX];
int n ;
int lowbit(int x){
return x&(-x);
}
void update( int value , int site ){
while( site <= n ){
c[site] = ( 0LL + c[site] + value ) % MOD;
site += lowbit(site);
}
}
int getsum(int i){
int sum = 0;
while( i >= 1 ){
sum = ( 0LL + sum + c[i] ) % MOD ;
i -= lowbit(i);
}
return sum ;
}
int main(){
int T;
scanf("%d",&T);
int Case = 0 ;
while( T-- ){
memset( dp, 0 , sizeof(dp) );
scanf("%d",&n);
int cur = 0 ;
for( int i = 1 ; i <= n ; i ++ ){
scanf("%d",&a[i]);
dp[cur][i] = 1 ;
}
printf("Case #%d: ",++Case);
printf("%d",n);
int f = 1 ;
for( int i = 2 ; i <= n ; i++ ){
cur = !cur;
LL ans = 0LL ;
if( f ){
memset( c , 0 , sizeof(c) );
for( int j = 1 ; j <= n ; j ++ ){
dp[cur][j] = getsum( a[j] - 1 ) % MOD;
ans = ( ans + dp[cur][j] ) % MOD;
update( dp[!cur][j] , a[j] );
}
}
if( !ans ) f = 0;
printf(" %d",ans%MOD);
}
printf("\n");
}
return 0 ;
}
思路:分别求红绿和蓝绿的最短路,然后前n-2条边肯定是不能组成n个点的最小生成树,所以都输出-1,然后分别求出每个最小生成树没有用过的边,排个序,最后就比较两个最小生成树加1,2…条边后谁更小,就输出谁。
Code:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 1e2+66;
struct Node{
int u , v , w ;
int id ;
bool friend operator < ( const Node &a , const Node &b ){
return a.w < b.w ;
}
}a[AX] , b[AX] , c[AX];
int pa[AX];
int pb[AX];
int vis[5][AX];
Node roada[AX];
Node roadb[AX];
int n , m ;
int tot1 , tot2;
void init(){
for( int i = 1 ; i <= n ; i++ ){
pa[i] = i ; pb[i] = i ;
}
memset( vis , 0 , sizeof(vis) );
tot1 = 0 ;
tot2 = 0 ;
}
int find_a( int x ){
return x == pa[x] ? pa[x] : pa[x] = find_a(pa[x]);
}
int find_b( int x ){
return x == pb[x] ? pb[x] : pb[x] = find_b(pb[x]);
}
void mix_a( int x , int y ){
int xx = find_a(x); int yy = find_a(y);
if( xx != yy ) pa[yy] = xx ;
}
void mix_b( int x , int y ){
int xx = find_b(x); int yy = find_b(y);
if( xx != yy ) pb[yy] = xx ;
}
int Kruscal( Node *e , int k , int id ){
int ans = 0 ;
int nEdge = 0 ;
sort( e , e + k );
for( int i = 0 ; i < k ; i++ ){
if( !id ){
if( find_a(e[i].u) != find_a(e[i].v) ){
mix_a( e[i].u , e[i].v );
ans += e[i].w;
vis[id][e[i].id] = 1 ;
nEdge ++;
}
}else{
if( find_b(e[i].u) != find_b(e[i].v) ){
mix_b( e[i].u , e[i].v );
ans += e[i].w;
vis[id][e[i].id] = 1 ;
nEdge ++;
}
}
}
if( nEdge < n - 1 ) ans = INF ;
return ans ;
}
int main(){
int T;
scanf("%d",&T);
int Case = 0 ;
while( T-- ){
scanf("%d%d",&n,&m);
init();
char col[5];
for( int i = 1 ; i <= m ; i++ ){
scanf("%d%d%d%s",&c[i].u,&c[i].v,&c[i].w,col);
if( col[0] == 'R' || col[0] == 'G' ){
a[tot1].u = c[i].u ; a[tot1].v = c[i].v; a[tot1].w = c[i].w ; a[tot1++].id = i;
}
if( col[0] == 'B' || col[0] == 'G' ){
b[tot2].u = c[i].u ; b[tot2].v = c[i].v; b[tot2].w = c[i].w ; b[tot2++].id = i;
}
}
int ans = Kruscal(a,tot1,0);
int ans1 = Kruscal(b,tot2,1);
int num = 0 ;
int cnt = 0 ;
for( int i = 1 ; i <= m ; i++ ){
if( !vis[0][i] ){
roada[num++] = c[i];
}
}
sort( roada , roada + num );
for( int i = 1 ; i <= m ; i++ ){
if( !vis[1][i] ){
roadb[cnt++] = c[i];
}
}
sort( roadb , roadb + cnt ) ;
printf("Case #%d:\n",++Case);
if( ans1 == INF && ans == INF ){
for( int i = 0 ; i < m ; i++ ){
printf("-1\n");
}continue;
}
for( int i = 1 ; i < n - 1 ; i++ ){
printf("-1\n");
}
printf("%d\n",min(ans1,ans));
int t = 0 ;
int t1 = 0 ;
for( int i = n ; i <= m ; i++ ){
ans += roada[t++].w;
ans1 += roadb[t1++].w;
printf("%d\n",(ans<ans1?ans:ans1));
}
}
return 0 ;
}