A.膜法记录
思路:注意到1<=n<=5,那么我们可以枚举行的消除情况,让后再判断列的消除是否可以满足。详情见代码.
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
char s[6][maxn];
int n,m,t,a,b;
int flag,vis[6];
bool C(){
set<int> c;
for(int i=1;i<=n;i++){
if(vis[i]) continue;
for(int j=0;j<m;j++)
if(s[i][j]=='*') c.insert(j);
}
return b>=c.size();
}
void dfs(int row, int num){
if(C()){
flag=1;return;
}
if(row>n||num>=a) return;
vis[row]=1;
dfs(row+1,num+1);
vis[row]=0;
dfs(row+1,num);
}
int main(){
cin>>t;
while(t--){
scanf("%d %d %d %d",&n,&m,&a,&b);
for(int i=1;i<=n;i++) scanf("%s",s[i]);
flag=0;
dfs(1,0);
if(flag) puts("yes");
else puts("no");
}
return 0;
}
B.阶乘
思路:对于p进行质因数分解,记录每一个质因数出现的情况,然后枚举每一个质因数出现那么多次数所需要的最小阶乘,然后选择最大的即可。
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn=3e5+5;
int p[maxn],p_n[maxn],cnt;
int solve(int num, int y){
int s=0;
for(int i=1;;i++){
s++;
int z=i;
while(z%y==0){
s++;z/=y;
}
if(s>=num) {
return y*i;
}
}
}
int main(){
ll x;
int t; scanf("%d",&t);
while(t--){
cnt=0;
scanf("%lld",&x);
if(x==1) {
printf("1\n"); continue;
}
//质因数分解
for(int i=2;i*i<=x;i++){
if(x%i==0){
p[++cnt]=i;p_n[cnt]=0;
while(x%i==0){
p_n[cnt]++;x/=i;
}
}
}
if(x>1) p[++cnt]=x,p_n[cnt]=1;
//枚举每一个质因数
int ans=0;
for(int i=1;i<=cnt;i++){
//cout<<p_n[i]<<" "<<p[i]<<endl;
ans=max(ans,solve(p_n[i],p[i]));
}
cout<<ans<<endl;
}
return 0;
}
C.完全图
思路:对于一个完全删除第一个点需要n-1条边,删除第二个点需要n-2条边,那么对于删除m条边最多删除的点,用二分查找。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m;
int main(){
int t; scanf("%d",&t);
while(t--){
scanf("%lld%lld",&n,&m);
ll l=0,r=n-1;
while(r>l){
ll mid=(l+r+1)>>1;
if(2*n-mid-1<=2*m/mid) l=mid;
else r=mid-1;
}
//删除l个点,形成l+1个连通分量
cout<<l+1<<endl;
}
}
G.树上求和
思路:求每一条边左右的点的数量x,y,根据权值x*y进行分配边的值。
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
int n;
vector<int> e[maxn];
ll a[maxn],t;
ll dfs(int u, int fa){
ll sum=1;
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v!=fa){ //使其仅向一侧遍历
sum+=dfs(v,u);
}
}
a[t++]=sum*(n-sum);
return sum;
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
int u,v; scanf("%d %d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,-1);
sort(a,a+t);
ll ans=0,k=1;
for(int i=t-1;i>=0;i--){
ans+=k*a[i];k++;
}
cout<<ans<<endl;
return 0;
}
H.奇怪的背包增加加了
思路:分治算法,详情见代码代码,很好理解的。
#include <bits/stdc++.h>
using namespace std;
int a[33],b[33],t;
int n;
int c[100005];
int solve(int x,int s){
if(x==1&&a[x]<s) return 0;
if(a[x]>=s){
a[x]-=s;
return 1;
} else{
s-=a[x];
a[x]=0;
}
return solve(x-1,s*2);
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(int i=0;i<n;i++){
scanf("%d",c+i);
b[c[i]]++;a[c[i]]++;
}
if(solve(30,1)){
for(int i=0;i<n;i++){
if(b[c[i]]>a[c[i]]){
printf("1"); a[c[i]]++;
} else printf("0");
}
printf("\n");
}
else puts("impossible");
}
return 0;
}