T1-简单的乘法
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
int n,m;
int a[N];
long double c[N] , pre[N];
long double maxn;
bool vis[N];
int cnt = 0;
void dfs(int k,int t,long double ans){
	if( t>m ){
		cnt ++ ;
		maxn = max( ans , maxn);
		return;
	}
	vis[k] = 1;
	for(int i=1;i<=n;i++){
		if( !vis[i] ){
			dfs( i,t+1, (ans+a[i])*c[t] );
		}
	}
	vis[k] = 0;
} 
int main(){
	freopen( "multi.in","r",stdin ); 
	freopen( "multi.out","w",stdout );
	cin>>n>>m;
	bool flag = 1; //标记 c中元素是否全部大于0 
	for(int i=1;i<=n;i++){
		cin>>a[i];	
	} 
	for(int i=1;i<=m;i++){
		cin>>c[i];
		if( c[i] < 0 ) flag = 0;
	} 
	if( n<=5 ) { // 测试点1~3,暴力枚举 
		long double ans = 0;
		for(int i=1;i<=m;i++){ //没有搞清楚题目描述,所以取一种情况的结果初始话答案ans 
			ans = (ans+a[i])*c[i];
		}
		maxn = ans;		
		dfs(0,1,0);
		cout << fixed << setprecision( 1 ) << maxn ;
	}
	else if( flag ){ //测试点 4~6 , 0<ci , 使用贪心策略
		sort( a+1,a+n+1 );
		pre[m+1] =  1 ;
		for(int i=m;i>=1;i--){
			pre[i] = pre[i+1]*c[i];
		}
		sort( pre+1,pre+m+1 );
		long double ans = 0;
		for(int i=m,j=n;i>=1;i--,j--){  
			ans += a[j]*pre[i]; 
		}	
		cout << fixed << setprecision( 1 ) << ans ;	
	}
	else{ //满分做法
		sort( a+1,a+n+1 );
		pre[m] =  c[m] ;
		for(int i=m-1;i>=1;i--){
			pre[i] = pre[i+1]*c[i];
		}
		sort( pre+1,pre+m+1); // 按p值排序
		long double ans = 0;
		/*
		    a  0 1 2 3 
			p -2 -1 0 1 2 
			从上面的数组中可以很容易看出最优策略
		*/
		for(int i=1;i<=m;i++){
			if( pre[i] <= 0 ){
				ans += a[i]*pre[i];
            }
			else{
				for(int j=m,k=n;j>=i;j--,k--){
					ans += a[k]*pre[j];
				}
                break;
			} 
		} 
		cout << fixed << setprecision( 1 ) << ans ;			
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}
	T2-0的个数
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6;
int n;
ll a[N];
int main(){
	freopen( "zero.in","r",stdin ); 
	freopen( "zero.out","w",stdout );
	cin>>n;
	if( n<=10 ){ // 50分写法 , 1<=n<=10 , 1<=ai<=10 
		ll re = 1;
		for(int i=1;i<=n;i++){
            cin>>a[i];
            re*=a[i]; 
        } 
		int cnt = 0;
		while( re%10 == 0 ){
            re/=10 ;
			cnt++;
		}
		cout << cnt ;
	}
	else{ //满分写法, 唯一分解定理,统计2和5的个数num2,num5,取两者最小值 
		ll num2=0,num5=0;
        int x;
		for(int i=1;i<=n;i++){
            cin>>a[i];
            int tmp = a[i];
			while( tmp%2==0 ) ++num2,tmp>>=1;
			while( tmp%5==0 ) ++num5,tmp/=5;
		} 
		cout << min( num2,num5 );
	}
        fclose(stdin);
        fclose(stdout);
        return 0;
}
	T3-黑洞2
	#include <bits/stdc++.h> 
using namespace std;
using ll = long long;
string s[200034];
ll n, m;
bool vis[50][50];
ll dx[4] = {-1, 1, 0, 0};
ll dy[4] = {0, 0, 1, -1};
vector<pair<ll, ll>> v;
bool check(ll x, ll y) { return x >= 0 && x < n && y >= 0 && y < m && s[x][y] != 'O'; }
//获取起点(sx,sy)的移动的偏移序列v 
void dfs(ll x, ll y, ll sx,ll sy){
  v.push_back({x - sx, y - sy});
  vis[x][y] = 1;
  for (ll i = 0; i < 4; i++) {
    ll nx = x + dx[i];
    ll ny = y + dy[i];
    if (check(nx, ny) && !vis[nx][ny]) 
	  dfs(nx, ny, sx, sy); 
  }
}
bool f(ll x, ll y) {
  memset(vis, 0, sizeof vis);
  v.clear();
  dfs(x, y, x, y); 
  //让其它每一个点按照(x,y)的偏移序列进行移动,验证是否会在按照(x,y)的移动过程中消失 
  for (ll i = 0; i < n; i++) {
    for (ll j = 0; j < m; j++) {
      if (s[i][j] == '.' && (i != x || j != y)) {
        bool flag = true;
        for (auto& p : v) { //模拟(i,j)的移动过程 
          ll nx2 = i + p.first;
          ll ny2 = j + p.second;
          if (!check(nx2, ny2)) { //消失后就可以停止移动 
            flag = false;
            break;
          }
        }
        if (flag) return false;//如果移动过程结束后,该点仍未消失,则不成立 
      }
    }
  }
  return true;
}
void solve(){
  cin >> n >> m;
  for (ll i = 0; i < n; i++) cin >> s[i]; 
  ll ans = 0;
  for(ll i = 0; i < n; i++)
    for(ll j = 0; j < m; j++) 
      if( s[i][j] == '.' && f(i, j) )  
	  	ans++; 
  cout << ans << '\n';
}
int main() {
  freopen("blackhole.in", "r", stdin);
  freopen("blackhole.out", "w", stdout);
  ll t; cin >> t; 
  while (t --)  solve();
  fclose(stdin);
  fclose(stdout);
  return 0;
}
	T4-植物大战僵尸
	
		30分写法:贪心
	
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
int t, n,m ; 
int a[N],h[N],c[10];
int solve(){
	memset( c,0,sizeof c );
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];	
	} 
	cin>>m;
	for(int i=1;i<=m;i++){
	 	cin>>h[i];
		c[h[i]]++;
	} 
	int k = 0;
	while( true ){
		int ak = a[k%n]; // 第k次的攻击力 
		if( ak==3 ){ // 3 6 5 4 2 1 
			if( c[3] ){ 
				c[3]--;
			}
			else if( c[5] ){
				c[5]--;
				c[2]++;
			}	
			else if( c[6] ){ 
				c[6]--;
				c[3]++;
			}					
			else if( c[4]){
				c[4]--;
				c[1]++;
			}
			else if( c[2]){
				c[2]--;
			}
			else if( c[1]){
				c[1]--;
			}
			else return k;
		}
		else { // 2 1 4 5 6 3
			if( c[2] ) c[2]--;
			else if( c[1] ) c[1]--;	//注意,如果没有血量为2的怪兽,那么应该优先处理血量为1的怪兽,因为攻击力2消灭血量为1的怪兽最合适 
			else if( c[4] ){
				c[4]--;
				c[2]++;
			}	
							
			else if( c[5] ){
				c[5]--;
				c[3]++;
			}
			else if( c[6] ){
				c[6]--;
				c[4]++;
			}						
			else if( c[3] ){
				c[3]--;
				c[1]++;
			}
			else return k;
		}
		k++;	
	}
	
}
int main(){
  	// 重定向标准输入输出到文件
  	freopen( "plant.in", "r", stdin);   // 输入文件 "plant.in"
  	freopen( "plant.out", "w", stdout); // 输出文件 "plant.out"
	cin>>t;        
	while(t--){
		cout << solve() << endl;
	}
	// 关闭文件流
	fclose(stdin);
	fclose(stdout);	
	return 0;
}
	50分写法:
对n==1的情况做出特殊判断即可
        if( n==1 ){
		long long ans = 0 , ak;
		cin>>ak; 
		cin>>m;
		for(int i=1;i<=m;i++){
		 	cin>>h[i];
			ans += (h[i]+ak-1)/ak;
		} 	
		return ans;		
	}
	100分写法:贪心+二分
#include <bits/stdc++.h>
using namespace std;
const int N = 1000100; 
using ll = long long;  // 使用 long long 并简写为 ll
ll n;                  // 循环节长度
ll a[N];         // 存储循环节内的攻击序列:每个值为2或3
ll m;                  // 僵尸数量
ll h[N];         // 每个僵尸的生命值
ll p2[N];        // 前缀和数组:p2[i] 表示前i次攻击中造成2点伤害的次数
ll p3[N];        // 前缀和数组:p3[i] 表示前i次攻击中造成3点伤害的次数
ll H[N];         // 临时数组,用于在模拟过程中修改生命值(避免破坏原始数据)
/**
 * 判断在给定 c2(2点伤害次数)和 c3(3点伤害次数)的情况下,
 * 是否可以消灭所有僵尸。
 *
 * @param c2 可用的2点伤害攻击次数
 * @param c3 可用的3点伤害攻击次数
 * @return 是否能消灭全部僵尸
 */
bool ok(ll c2, ll c3) {
  // 复制原始生命值到临时数组 H,以便后续修改
  memcpy( H,h,sizeof h ); 
  /*
   * 第一阶段:优先使用3点伤害处理奇数血量且 >1 的僵尸
   * 理由:如果一个僵尸血量是奇数(比如5),只用2点伤害会多出1点剩余(无法击杀)
   * 所以需要至少一次3点伤害来补正。特别注意:血量为1的情况不需要3点伤害。
   */
  for (ll i = 1; i <= m; i++) {
    if ( H[i] % 2 == 1 && H[i] > 1 && c3) {  // 血量为奇数且>1,并且还有3点伤害可用
      H[i] -= 3;//血量减少3 
      c3--;  // 消耗一次3点伤害
    }
  }
  /*
   * 第二阶段:批量处理每6点血量可以用两个3点伤害解决的问题
   * 因为 3+3=6 正好消去6点血,效率最高。
   * 尽可能多地将血量减少成不超过4的余数。
   */
  for (ll i = 1; i <= m && c3 ; i++) {
    ll x3 = H[i] / 6 * 2;  // 最多能用多少次3点伤害来处理完整的6点段
    ll use = min(c3, x3);  // 实际可使用的3点伤害次数
    H[i] -= 3 * use;
    c3 -= use;
  }
  /*
   * 第三阶段:处理剩余血量恰好为4的僵尸(最浪费3点伤害的情形)
   * 因为4=2+2,最优策略是用两次2点伤害,但如果只剩3点伤害可用,
   * 不得不用3点打一次(剩1血),所以这里先尝试用3点打4血僵尸,
   * 避免留下无法处理的1血残血。
   */
//  for (ll i = 1; i <= m && c3; i++) {
//    if (H[i] == 4) {
//      H[i] -= 3;
//      c3--;
//    }
//  }
  /*
   * 将剩下的所有3点伤害转换为等效的2点伤害:
   * 因为每次3点伤害可以视为“一次2点伤害 + 多出1点伤害”,
   * 但既然已经尽可能用了3点伤,现在可以把它们当作2点伤来填补空白。
   * 注意:此处是贪心的关键——只要总2点当量足够,就可以完成清理。
   */
  c2 += c3;  // 把剩余未用的3点伤害也当作“相当于提供c3次2点伤害”
  c3 = 0;
  /*
   * 第四阶段:最后统一用2点伤害收尾
   * 对于每个还没死的僵尸,计算所需最少2点伤害次数:ceil(H[i] / 2)
   * 即 (H[i] + 1)/2 向上取整除以2
   */
  for (ll i = 1; i <= m; i++) {
    if (H[i] == 0) continue;  // 已死亡,跳过
    else {
      ll x2 = (H[i] + 1) / 2;  // ceil(H[i]/2),即需要多少个2点伤害才能打死
      if (c2 >= x2) {
        c2 -= x2;     // 消耗相应数量的2点伤害机会
        H[i] = 0;     // 僵尸被打死
      } else {
        return false; // 2点伤害不够,无法全清
      }
    }
  }
  return true;  // 成功消灭所有僵尸
}
/**
 * 解决单组测试用例
 */
void solve() {
  cin >> n;
  // 读入循环节的 n 个攻击数值
  for (ll i = 1; i <= n; i++) {
    cin >> a[i];
    // 构建前缀和:
    // p2[i]: 前i个攻击中有多少次伤害为2
    // p3[i]: 前i个攻击中有多少次伤害为3
    p2[i] = p2[i - 1] + (a[i] == 2);
    p3[i] = p3[i - 1] + (a[i] == 3);
  }
  cin >> m;  // 读入僵尸数量
  for (ll i = 1; i <= m; i++) {
    cin >> h[i];  // 读入每个僵尸的血量
  }
  // 二分查找最小攻击次数
  ll l = 0, r = 1e18;  // 设定搜索范围:从0到一个极大值
  while (l < r) {
    ll mid = (l + r) >> 1;  // 二分中点
    // 计算 mid 次攻击中总共产生的2点和3点伤害次数:
    // 整周期部分:mid / n 个完整循环
    // 剩余部分:mid % n 次攻击
    ll c2 = mid / n * p2[n] + p2[mid % n]; // c2: 攻击力2的攻击次数; 
    ll c3 = mid / n * p3[n] + p3[mid % n]; // c3: 攻击力3的攻击次数;
    if (ok(c2, c3)) {
      r = mid;  // 可行,尝试更小的次数
    } else {
      l = mid + 1;  // 不可行,需增加攻击次数
    }
  }
  cout << l << '\n';  // 输出最少攻击次数
}
int main() {
  freopen( "plant.in", "r", stdin);   // 输入文件 
  freopen( "plant.out", "w", stdout); // 输出文件 
  ll t;
  cin >> t;  // 读入测试数据组数
  while (t--) {
    solve();  // 处理每组数据
  }
  // 关闭文件流
  fclose(stdin);
  fclose(stdout);
  return 0;
}