题意:求数位总和为s且是d的倍数的最小值,若无解输出-1
思路:s<=5000,d<=500说明状态数<=s*d=5000*500,bfs搜索
代码:
#include<bits/stdc++.h>
using namespace std;
struct node
{
int mod;
int sum;
string num;
};
bool vis[5010][510];
int main()
{
for ( int d,s ; scanf( "%d%d" , &d , &s )==2 ; )
{
memset ( vis , false , sizeof(vis) );
queue<node>Q;
node p,q;
p.mod = 0;
p.sum = 0;
p.num = "";
Q.push(p);
vis[0][0] = true;
bool ans = false;
while ( !Q.empty() )
{
p = Q.front(); Q.pop();
if ( p.sum==s&&p.mod==0 )
{
ans = true;
cout<<p.num<<endl;
}
for ( int i=0 ; i<=9 ; i++ )
{
q = p;
q.mod = (q.mod*10+i)%d;
q.sum = (q.sum+i);
q.num += ('0'+i);
if ( q.sum<=s&&!vis[q.sum][q.mod] )
{
vis[q.sum][q.mod] = true;
Q.push(q);
}
}
}
if ( !ans ) cout<<"-1"<<endl;
}
return 0;
}
题意:n,k,m,m个区间任务L,R表示执行时间,C表示需求机器数,P表示单台机器价格,每个时间点最多使用K台机器,求最小花费
思路:以花费排序维护两个树状数组,树状数组A维护花费的数目,树状数组B维护花费的总和,顺序遍历时间点,先二分满足K台数目以上的花费值,然后查询该花费值的总和
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 200010;
const int maxm = 400010;
typedef long long LL;
struct node
{
int l,r,c,p;
}a[maxn];
int b[maxm],bb[maxn],n,k,m,mm;
vector< pair< int,pair<int,int> > >G[maxm];
LL bita[maxn],bitb[maxn],ans;
int lowbit( int x )
{
return x&(-x);
}
void add( int u , LL a , LL b )
{
for ( ; u<=mm ; u+=lowbit(u) )
{
bita[u] += a;
bitb[u] += b;
}
}
LL suma( int u )
{
LL res = 0;
for ( ; u>=1 ; u-=lowbit(u) )
res += bita[u];
return res;
}
LL sumb( int u )
{
LL res = 0;
for ( ; u>=1 ; u-=lowbit(u) )
res += bitb[u];
return res;
}
int main()
{
scanf ( "%d%d" , &n , &k ); scanf ( "%d" , &n );
for ( int i=0 ; i<n ; i++ )
{
scanf( "%d%d" , &a[i].l , &a[i].r );
scanf( "%d%d" , &a[i].c , &a[i].p );
a[i].r++;
b[i*2+0] = a[i].l;
b[i*2+1] = a[i].r;
bb[i] = a[i].p;
}
sort ( b , b+n+n );
m = unique( b , b+n+n )-b;
/*
for ( int i=0 ; i<m ; i++ )
printf ( "%d " , b[i] );
*/
//printf( "\n" );
for ( int i=0 ; i<n ; i++ )
{
int tl = lower_bound( b , b+m , a[i].l )-b;
int tr = lower_bound( b , b+m , a[i].r )-b;
G[tl].push_back( make_pair(+1,make_pair(a[i].c,a[i].p)) );
G[tr].push_back( make_pair(-1,make_pair(a[i].c,a[i].p)) );
}
for ( int i=0 ; i<m ; i++ )
sort( G[i].begin() , G[i].end() );
/*
for ( int i=0 ; i<m ; i++ )
{
int sz = G[i].size();
for ( int j=0 ; j<sz ; j++ )
printf ( "%d %d %d %d\n" , i , G[i][j].first , G[i][j].second.first , G[i][j].second.second );
}
*/
sort ( bb , bb+n );
mm = unique( bb , bb+n )-bb;
/*
printf ( "%d\n" , mm );
for ( int i=0 ; i<mm ; i++ )
printf ( "%d " , bb[i] );
printf ( "\n" );
*/
for ( int i=1 ; i<=mm ; i++ )
{
bita[i] = 0;
bitb[i] = 0;
}
ans = 0;
for ( int i=0 ; i<m ; i++ )
{
if ( i!=0 )
{
if ( suma(mm)<=k )
{
ans += 1LL*(b[i]-b[i-1])*sumb(mm);
//printf ( "%d %I64d %I64d\n" , b[i] , suma(mm) , sumb(mm) );
}
else
{
int l=1,r=mm;
while ( l<=r )
{
int mid = (l+r)>>1;
if ( suma(mid)>=k ) r = mid-1;
else l = mid+1;
}
//printf ( "%d %d %d\n" , b[i] , l , bb[l-1] );
//printf ( "%I64d\n" , suma(l) );
//printf ( "%I64d\n" , suma(l-1) );
ans += 1LL*(b[i]-b[i-1])*sumb(l-1);
if ( suma(l)>=k ) ans += 1LL*(b[i]-b[i-1])*(k-suma(l-1))*bb[l-1];
}
}
//printf ( "%I64d\n" , ans );
int sz = G[i].size(),now;
for ( now=0 ; now<sz&&G[i][now].first<0 ; now++ )
{
int cc = G[i][now].second.first;
int pp = G[i][now].second.second;
//printf ( "- %d %d %d\n" , i , cc , pp );
int qq = lower_bound( bb , bb+mm , pp )-bb;
//printf ( "%d %d %d %d\n" , i , cc , pp , qq );
//printf ( "%d %d\n" , b[i] , G[i][now].first );
//printf ( "%d\n" , qq+1 );
add( qq+1 , -cc , -1LL*cc*pp );
}
for ( ; now<sz ; now++ )
{
int cc = G[i][now].second.first;
int pp = G[i][now].second.second;
int qq = lower_bound( bb , bb+mm , pp )-bb;
//printf ( "+ %d %d %d\n" , i , cc , pp );
add( qq+1 , cc , 1LL*cc*pp );
}
}
printf ( "%I64d\n" , ans );
return 0;
}
题意:有n天每天有ai个货物需要打包存仓,每个包裹最多存k个,货物可以当天打包或者推迟一天,求最少使用包裹数
思路:每天最多保留ai个货物,遍历计算
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main()
{
int n,k;
scanf( "%d%d" , &n , &k );
LL ans = 0,need = 0;
for ( int i=1 ; i<=n ; i++ )
{
int x;
scanf( "%d" , &x );
need += x;
int low = ( need-x )/k;
if ( (need-x)%k!=0 ) low++;
ans += low;
need -= 1LL*low*k;
if ( need<0 ) need = 0;
}
ans += need/k;
if ( need%k!=0 ) ans++;
printf( "%I64d\n" , ans );
return 0;
}
题意:n个人有各自支持意向(11,10,01,00)和价值,从中选取一些人使得A的支持者大于等于总人数的一半B的支持这大于等于总人数的一半,求最大价值和
思路:贪心
代码:
#include<bits/stdc++.h>
using namespace std;
int Abs( int a ){ return a<0?-a:a; }
vector<int>a[4];
int cal( char s[] )
{
return (s[0]-'0')*2+(s[1]-'0');
}
int main()
{
int n;
scanf( "%d" , &n );
char s[5]; int x;
for ( int i=0 ; i<n ; i++ )
{
scanf ( "%s%d" , s , &x );
a[cal(s)].push_back(x);
//printf( "%d %d\n" , cal(s) , x );
}
for ( int i=1 ; i<4 ; i++ )
sort( a[i].begin() , a[i].end() );
int ans = 0;
int sz3 = a[3].size();
for ( int i=0 ; i<sz3 ; i++ )
ans += a[3][i];
//printf ( "%d\n" , ans );
int sz1 = a[1].size();
int sz2 = a[2].size();
int dif = Abs(sz1-sz2);
if ( sz1<sz2 )
{
for ( int i=0 ; i<sz1 ; i++ )
ans += a[1][i]+a[2][i+dif];
for ( int i=0 ; i<dif ; i++ )
a[0].push_back(a[2][i]);
}
else
{
for ( int i=0 ; i<sz2 ; i++ )
ans += a[1][i+dif]+a[2][i];
for ( int i=0 ; i<dif ; i++ )
a[0].push_back(a[1][i]);
}
//printf ( "%d\n" , ans );
sort ( a[0].begin() , a[0].end() );
int sz0 = a[0].size();
for ( int i=sz0-1 ; i>=0&&sz3 ; i--,sz3-- )
ans += a[0][i];
printf( "%d\n" , ans );
return 0;
}
题意:有n个文本,q个询问,每次询问一个文本求它是几个文本的子串
思路:先将文本和询问读入,对询问建立AC自动机,对文本做询问
代码:
#include<bits/stdc++.h>
using namespace std;
char f[10010][10];
int ans1[50010],ans2[50010];
int change( char ch )
{
if ( ch>='0'&&ch<='9' ) return ch-'0';
if ( ch>='a'&&ch<='z' ) return ch-'a'+10;
if ( ch=='.' ) return 36;
}
struct ACauto
{
int next[400010][37],fail[400010],root,L;
vector<int>end[400010];
int newNode()
{
for( int i=0 ; i<37 ; i++ )
next[L][i] = -1;
end[L++].clear();
return L-1;
}
void init()
{
L = 0; root = newNode();
}
void Insert( int id , char *s )
{
int len = strlen(s);
int now = root;
for( int i=0 ; i<len ; i++ )
{
int tmp = change(s[i]);
if ( next[now][tmp]==-1 )
next[now][tmp] = newNode();
now = next[now][tmp];
}
end[now].push_back(id);
}
void Build()
{
queue<int>Q; fail[root] = root;
for( int i=0 ; i<37 ; i++ )
{
if ( next[root][i]==-1 )
next[root][i] = root;
else
{
fail[next[root][i]] = root;
Q.push(next[root][i]);
}
}
while( !Q.empty() )
{
int now = Q.front(); Q.pop();
for ( int i=0 ; i<37 ; i++ )
{
if ( next[now][i]==-1 )
next[now][i] = next[fail[now]][i];
else
{
fail[next[now][i]] = next[fail[now]][i];
Q.push(next[now][i]);
}
}
}
}
void Query( int id , char *s )
{
int len = strlen(s);
int now = root;
for ( int i=0 ; i<len ; i++ )
{
int tmp = change(s[i]);
now = next[now][tmp];
int nxt = now;
while ( nxt!=root )
{
if( end[nxt].size()&&ans2[end[nxt][0]]!=id )
{
int sz = end[nxt].size();
for ( int j=0 ; j<sz ; j++ )
{
ans1[end[nxt][j]]++;
ans2[end[nxt][j]] = id;
}
}
nxt = fail[nxt];
}
}
}
}AC;
int main()
{
int n;
scanf( "%d" , &n );
for ( int i=0 ; i<n ; i++ )
scanf ( "%s" , f[i] );
int q;
scanf( "%d" , &q );
for ( int i=0 ; i<q ; i++ )
ans1[i] = 0,ans2[i] = -1;
char s[10];
AC.init ();
for ( int i=0 ; i<q ; i++ )
{
scanf ( "%s" , s );
AC.Insert( i , s );
}
AC.Build();
for ( int i=0 ; i<n ; i++ )
AC.Query( i , f[i] );
for ( int i=0 ; i<q ; i++ )
{
printf ( "%d " , ans1[i] );
if ( ans2[i]==-1 ) printf( "-\n" );
else printf ( "%s\n" , f[ans2[i]] );
}
return 0;
}
题意:长度为n的数组要求分成k段且每段的和相等,有解输出各段长度无解输出-1
思路:模拟一下一个K一个K向下找
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int a[100010],b[100010];
int main()
{
int n,k;
scanf( "%d%d" , &n , &k );
a[0] = 0;
for ( int i=1 ; i<=n ; i++ )
{
scanf ( "%d" , &a[i] );
a[i] = a[i-1]+a[i];
}
bool ok = true;
//printf ( "%d %d\n" , a[n] , k );
if ( a[n]%k!=0 ) ok = false;
else
{
b[0] = 0;
int pos = 1,add = a[n]/k;
for ( int i=1 ; i<=k ; i++ )
{
while ( a[pos]<i*add ) pos++;
if ( a[pos]!=i*add )
{
ok = false; break;
}
else
{
b[i] = pos;
}
}
}
if ( !ok ) printf ( "No\n" );
else
{
printf ( "Yes\n" );
for ( int i=1 ; i<=k ; i++ )
{
if ( i!=1 ) printf( " " );
printf ( "%d" , b[i]-b[i-1] );
}
printf( "\n" );
}
return 0;
}