模板

#include <bits/stdc++.h>
#include <iostream>  
#include <cstdio>  
#include <algorithm>  
#include <cstring>  
#include <cmath>  
#include <map>  
#include <queue>  
#include <set>  
#include <vector>  
using namespace std;  
#define L(x) (1 << (x))  
const double PI = acos(-1.0); 
typedef long long LL; 
#define INF MAX_INT
copy(buf,n,str.c_str)//string转char
sscanf(buf,"%d%d",&a,&b)//串输入处理
sprintf(but,"%.2lf",n)//double转char * 
char *itoa(int value, char* string, int radix)//int转char* radix进制  
//优读
int read(){
    int x=0,f=1;
    char ch;
    while(ch<'0'||ch>'9')  {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return f*x;
}
template<class T>void read(T &x){//read(n) 
    x=0;int f=0;char ch=getchar();
    while(ch<'0'||ch>'9')  {f|=(ch=='-');ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x=f?-x:x;
    return;
}
//大数乘法 
string mult(string s,int n){
	int i,len = s.length(),temp = 0; 
	for(i = len - 1;i >= 0;i--)
	{
		temp = temp + (s[i] - '0')*n;
		s[i] = (temp%10)+'0';
		temp =  temp/10;//进位 
	}
	char c;
	while(temp)
	{
		c = (temp%10)+'0';
		s = c + s;
		temp /= 10;
	}
	return s;
}
//大数除法 
string div(string a, long long b){//高精度a除以单精度b
	string r, ans;
	long long d = 0;
	if (a == "0") return a;//特判
	for (int i = 0; i<a.size(); i++)
	{
		r += (d * 10 + a[i] - '0') / b + '0';//求出商
		d = (d * 10 + (a[i] - '0')) % b;//求出余数
	}
	long long p = 0;
	for (int i = 0; i<r.size(); i++)
		if (r[i] != '0') { p = i; break; }
	return r.substr(p);
}
//fft优化的大数乘法 
const int Maxn = 133015;  
double ax[Maxn], ay[Maxn], bx[Maxn], by[Maxn];  
char sa[Maxn/2],sb[Maxn/2];  
int sum[Maxn];  
int x1[Maxn],x2[Maxn];  
int revv(int x, int bits)  
{  
    int ret = 0;  
    for (int i = 0; i < bits; i++)  
    {  
        ret <<= 1;  
        ret |= x & 1;  
        x >>= 1;  
    }  
    return ret;  
}  
void fft(double * a, double * b, int n, bool rev)  
{  
    int bits = 0;  
    while (1 << bits < n) ++bits;  
    for (int i = 0; i < n; i++)  
    {  
        int j = revv(i, bits);  
        if (i < j)  
            swap(a[i], a[j]), swap(b[i], b[j]);  
    }  
    for (int len = 2; len <= n; len <<= 1)  
    {  
        int half = len >> 1;  
        double wmx = cos(2 * PI / len), wmy = sin(2 * PI / len);  
        if (rev) wmy = -wmy;  
        for (int i = 0; i < n; i += len)  
        {  
            double wx = 1, wy = 0;  
            for (int j = 0; j < half; j++)  
            {  
                double cx = a[i + j], cy = b[i + j];  
                double dx = a[i + j + half], dy = b[i + j + half];  
                double ex = dx * wx - dy * wy, ey = dx * wy + dy * wx;  
                a[i + j] = cx + ex, b[i + j] = cy + ey;  
                a[i + j + half] = cx - ex, b[i + j + half] = cy - ey;  
                double wnx = wx * wmx - wy * wmy, wny = wx * wmy + wy * wmx;  
                wx = wnx, wy = wny;  
            }  
        }  
    }  
    if (rev)  
    {  
        for (int i = 0; i < n; i++)  
            a[i] /= n, b[i] /= n;  
    }  
}  
int solve(int a[],int na,int b[],int nb,int ans[])  
{  
    int len = max(na, nb), ln;  
    for(ln=0; L(ln)<len; ++ln);  
    len=L(++ln);  
    for (int i = 0; i < len ; ++i)  
    {  
        if (i >= na) ax[i] = 0, ay[i] =0;  
        else ax[i] = a[i], ay[i] = 0;  
    }  
    fft(ax, ay, len, 0);  
    for (int i = 0; i < len; ++i)  
    {  
        if (i >= nb) bx[i] = 0, by[i] = 0;  
        else bx[i] = b[i], by[i] = 0;  
    }  
    fft(bx, by, len, 0);  
    for (int i = 0; i < len; ++i)  
    {  
        double cx = ax[i] * bx[i] - ay[i] * by[i];  
        double cy = ax[i] * by[i] + ay[i] * bx[i];  
        ax[i] = cx, ay[i] = cy;  
    }  
    fft(ax, ay, len, 1);  
    for (int i = 0; i < len; ++i)  
        ans[i] = (int)(ax[i] + 0.5);  
    return len;  
}  
string mul(string sa,string sb)  
{  
    int l1,l2,l;  
    int i;  
    string ans;  
    memset(sum, 0, sizeof(sum));  
    l1 = sa.size();  
    l2 = sb.size();  
    for(i = 0; i < l1; i++)  
        x1[i] = sa[l1 - i - 1]-'0';  
    for(i = 0; i < l2; i++)  
        x2[i] = sb[l2-i-1]-'0';  
    l = solve(x1, l1, x2, l2, sum);  
    for(i = 0; i<l || sum[i] >= 10; i++) // 进位  
    {  
        sum[i + 1] += sum[i] / 10;  
        sum[i] %= 10;  
    }  
    l = i;  
    while(sum[l] <= 0 && l>0)    l--; // 检索最高位  
    for(i = l; i >= 0; i--)    ans+=sum[i] + '0'; // 倒序输出  
    return ans;  
}  
//大数加法
string add(string s1,string s2){

	if(s1 == ""&&s2 == "")return "0";
	if(s1 == "")return s2;
	if(s2 == "")return s1;	
	string maxx = s1,minn = s2;
	if(s1.length() < s2.length())
	{
		maxx = s2;
		minn = s1;
	}
	int i,a = maxx.length()-1,b = minn.length()-1;
	for(i = b;i >=0 ;--i)
	{
		maxx[a--] += minn[i] - '0';
	}
	for(i = maxx.length() - 1;i > 0;--i)
	{
		if(maxx[i] > '9')
		{
			maxx[i] -= 10;
			maxx[i-1]++;
		}
	}
	
	if(maxx[0] > '9')
	{
		maxx[0] -= 10;
		maxx = '1'+maxx;
	}
	
	return maxx;
}
//大数减法
string bigIntegerSub(char *s1,char *s2){
    if(s1 == s2)
        return "0"; //相等
    int len1 = strlen(s1),len2 = strlen(s2);
    if(len1 > len2)
        return subInfo(s1,s2);
    else if(len1 < len2)
        return "-" + subInfo(s2,s1); //负数
    else {                        //长度相等时判断大小
        for(int i = 0; i < len1; i++){
            if(s1[i]-'0' > s2[i]-'0')
                return subInfo(s1,s2);
            else if(s1[i]-'0' < s2[i]-'0')
                return "-" + subInfo(s2,s1);
        }
    }
}
 //只限大的非负整数减小的非负整数  
const int L=110;  
string sub(string a,string b)
{  
    string ans;  
    int na[L]={0},nb[L]={0};  
    int la=a.size(),lb=b.size();  
    for(int i=0;i<la;i++) na[la-1-i]=a[i]-'0';  
    for(int i=0;i<lb;i++) nb[lb-1-i]=b[i]-'0';  
    int lmax=la>lb?la:lb;  
    for(int i=0;i<lmax;i++)  
    {  
        na[i]-=nb[i];  
        if(na[i]<0) na[i]+=10,na[i+1]--;  
    }  
    while(!na[--lmax]&&lmax>0)  ;lmax++;  
    for(int i=lmax-1;i>=0;i--) ans+=na[i]+'0';  
    return ans;  
}
//高精度求模 
int mod(string a,int b)//高精度a除以单精度b  
{  
    int d=0;  
    for(int i=0;i<a.size();i++) d=(d*10+(a[i]-'0'))%b;//求出余数  
    return d;  
}  
//高精度阶乘
const int L=100005;  
int a[L];  
string fac(int n)  
{  
    string ans;  
    if(n==0) return "1";  
    fill(a,a+L,0);  
    int s=0,m=n;  
    while(m) a[++s]=m%10,m/=10;  
    for(int i=n-1;i>=2;i--)  
    {  
        int w=0;  
        for(int j=1;j<=s;j++) a[j]=a[j]*i+w,w=a[j]/10,a[j]=a[j]%10;  
        while(w) a[++s]=w%10,w/=10;  
    }  
    while(!a[s]) s--;  
    while(s>=1) ans+=a[s--]+'0';  
    return ans;  
}
//快速幂 
ll powermod(ll a,ll b){
	ll res = 1;
	while(b)
	{
		if(b&1) res *= a;
		a *= a;
		b >>=1;
	} 
	return res;
}
//高精度快速幂
const int Maxn = 133015;  
double ax[Maxn], ay[Maxn], bx[Maxn], by[Maxn];  
char sa[Maxn/2],sb[Maxn/2];  
int sum[Maxn];  
int x1[Maxn],x2[Maxn];  
int revv(int x, int bits)  
{  
    int ret = 0;  
    for (int i = 0; i < bits; i++)  
    {  
        ret <<= 1;  
        ret |= x & 1;  
        x >>= 1;  
    }  
    return ret;  
}  
void fft(double * a, double * b, int n, bool rev)  
{  
    int bits = 0;  
    while (1 << bits < n) ++bits;  
    for (int i = 0; i < n; i++)  
    {  
        int j = revv(i, bits);  
        if (i < j)  
            swap(a[i], a[j]), swap(b[i], b[j]);  
    }  
    for (int len = 2; len <= n; len <<= 1)  
    {  
        int half = len >> 1;  
        double wmx = cos(2 * PI / len), wmy = sin(2 * PI / len);  
        if (rev) wmy = -wmy;  
        for (int i = 0; i < n; i += len)  
        {  
            double wx = 1, wy = 0;  
            for (int j = 0; j < half; j++)  
            {  
                double cx = a[i + j], cy = b[i + j];  
                double dx = a[i + j + half], dy = b[i + j + half];  
                double ex = dx * wx - dy * wy, ey = dx * wy + dy * wx;  
                a[i + j] = cx + ex, b[i + j] = cy + ey;  
                a[i + j + half] = cx - ex, b[i + j + half] = cy - ey;  
                double wnx = wx * wmx - wy * wmy, wny = wx * wmy + wy * wmx;  
                wx = wnx, wy = wny;  
            }  
        }  
    }  
    if (rev)  
    {  
        for (int i = 0; i < n; i++)  
            a[i] /= n, b[i] /= n;  
    }  
}  
int solve(int a[],int na,int b[],int nb,int ans[])  
{  
    int len = max(na, nb), ln;  
    for(ln=0; L(ln)<len; ++ln);  
    len=L(++ln);  
    for (int i = 0; i < len ; ++i)  
    {  
        if (i >= na) ax[i] = 0, ay[i] =0;  
        else ax[i] = a[i], ay[i] = 0;  
    }  
    fft(ax, ay, len, 0);  
    for (int i = 0; i < len; ++i)  
    {  
        if (i >= nb) bx[i] = 0, by[i] = 0;  
        else bx[i] = b[i], by[i] = 0;  
    }  
    fft(bx, by, len, 0);  
    for (int i = 0; i < len; ++i)  
    {  
        double cx = ax[i] * bx[i] - ay[i] * by[i];  
        double cy = ax[i] * by[i] + ay[i] * bx[i];  
        ax[i] = cx, ay[i] = cy;  
    }  
    fft(ax, ay, len, 1);  
    for (int i = 0; i < len; ++i)  
        ans[i] = (int)(ax[i] + 0.5);  
    return len;  
}  
string mul(string sa,string sb)  
{  
    int l1,l2,l;  
    int i;  
    string ans;  
    memset(sum, 0, sizeof(sum));  
    l1 = sa.size();  
    l2 = sb.size();  
    for(i = 0; i < l1; i++)  
        x1[i] = sa[l1 - i - 1]-'0';  
    for(i = 0; i < l2; i++)  
        x2[i] = sb[l2-i-1]-'0';  
    l = solve(x1, l1, x2, l2, sum);  
    for(i = 0; i<l || sum[i] >= 10; i++) // 进位  
    {  
        sum[i + 1] += sum[i] / 10;  
        sum[i] %= 10;  
    }  
    l = i;  
    while(sum[l] <= 0 && l>0)    l--; // 检索最高位  
    for(i = l; i >= 0; i--)    ans+=sum[i] + '0'; // 倒序输出  
    return ans;  
}  
string Pow(string a,int n)  
{  
    if(n==1) return a;  
    if(n&1) return mul(Pow(a,n-1),a);  
    string ans=Pow(a,n/2);  
    return mul(ans,ans);  
} 
//矩阵快速幂
struct Matrix  {
    int m[N][N];
    Matrix(){}
};
void Init(Matrix &matrix){
    for(int i = 0;i < N; i++)scanf("%d",&matrix.m[0][i]);
    for(int i = 1;i < N; i++){
        for(int j = 0;j < N; j++){
            if(i == (j+1))matrix.m[i][j] = 1;
            else matrix.m[i][j] = 0;
        }
    }
}
Matrix Mul(Matrix &a,Matrix &b){//矩阵相乘
    Matrix c;
    for(int i = 0; i < N; i++){
        for(int j = 0;j < N; j++){
            c.m[i][j] = 0;
            for(int k = 0; k < N; k++)c.m[i][j] += a.m[i][k]*b.m[k][j];
            c.m[i][j] %= Mod;
        }
    }
    return c;
}
Matrix Pow(Matrix& matrix, int k) {//矩阵幂
    Matrix res;
    for (int i = 0; i < N; ++i)
        for (int j = 0; j < N; ++j)
            if (i == j) res.m[i][j] = 1;
            else res.m[i][j] = 0;
    while (k) {
        if (k & 1) res = Mul(matrix,res);
        k >>= 1;
        matrix = Mul(matrix,matrix);
    }
    return res;
}
//线性筛 
int prime[maxn]; //标识 
int *p = new int[maxn/2+1];//素数数组 
int fun(){
	memset(prime,0,sizeof(int)*(maxn+1));
	memset(p,0,sizeof(int)*(maxn/2));
	int i,j,t = 0;
	for(i = 2;i <= maxn;i++)
	{
		if(prime[i] == 0)
		{
			p[t++] = i;
		}
		for(j = 0;j < t && i*p[j] <= maxn;j++)
		{
			prime[i*p[j]] = 1;
			if(i%p[j] == 0)break;
		}
	}
	return t;
}
//gcd
int gcd(int a,int b){
	if(b > a)swap(a,b);
	int temp;
	while(b != 0)
	{
		if(b > a)swap(a,b);
		temp = a % b;
		a = b;
		b = temp;
	} 
	return a;
} 
//扩展gcd
LL exgcd(LL a,LL b,LL &x,LL &y){
    if(b == 0){
        x = 1;
        y = 0;
        return a;
    }
    LL d = exgcd(b,a%b,x,y);
    LL t = x;
    x = y;
    y = t-(a/b)*y;
    return d;
}
//卢卡斯逆元
LL Lucas(LL n,LL k)     //Lucas定理递归   
{  
    if (k == 0)     //递归终止条件   
        return 1;  
    else  
        return C(n % mod , k % mod) * Lucas(n / mod , k / mod) % mod;  
}  
//组合数公式 
LL C(LL n , LL k)       //费马小定理求逆元   
{  
    if (k > n)  
        return 0;  
    else  
        return fac[n] * (quick(fac[k] * fac[n-k] % mod , mod - 2)) % mod;  
}   
//欧拉函数   对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目(φ(1)=1)
int phi[maxn+1], phi_psum[maxn+1];
int euler_phi(int n){
    int m = sqrt(n+0.5);
    int ans = n;
    for(int i = 2; i < m; i++)if(n % i == 0){
        ans = ans/i*(i-1);
        while(n % i == 0)n /= i;
    }
    if(n > 1)ans = ans/n*(n-1);
    return ans;
}
void phi_table(int n) {//筛素数的方法,求解1~n所有数的欧拉phi函数值
    phi[1] = 0; //这里不计算第一个1,1和1,1是重复的,等下直接2*phi_psum[n] + 1
    for(int i = 2; i <= n; i++) if(phi[i] == 0)
        for(int j = i; j <= n; j += i) {
            if(phi[j] == 0) phi[j] = j;
            phi[j] = phi[j] / i * (i-1);
        }
}
//二分法
int cmp(const void *a, const void *b) {
    return *(int *) a - *(int *) b;
}
int bs(int *arr,int L,int R,int target){//普通的二分查找
    while( L <= R){
        int mid = (L) + (R-L)/2;
        if(arr[mid] == target)
            return mid;
        if(arr[mid] > target)
            R = mid - 1;
        else
            L = mid + 1;
    }
    return -1; // not find
}
int firstEqual(int arr[], int L, int R, int target) {//求最小的i,使得a[i] = target,若不存在,则返回-1
    while (L < R) {
        int mid = L + (R - L) / 2;
        if (arr[mid] < target)
            L = mid + 1;
        else
            R = mid;
    }
    if (arr[L] == target)
        return L;
    return -1;
}
int lastEqualNext(int arr[], int L, int R, int target) {//求最大的i的下一个元素的下标(c++中的upperbound函数),使得a[i] = target,若不存在,则返回-1
    while (L < R) {
        int m = L + (R - L) / 2;
        if (arr[m] <= target) 
            L = m + 1;
        else
            R = m;
    }
    if (arr[L - 1] == target)
        return L;
    return -1;
}
int lastEqual(int arr[], int L, int R, int target) {//求最大的i,使得a[i] = target,若不存在,则返回-1
    while (L < R) {
        int mid = L + ((R + 1 - L) >> 1);//向上取整
        if (arr[mid] <= target)
            L = mid;
        else
            R = mid - 1;
    }
    if (arr[L] == target)
        return L;
    return -1;
}
int firstLarge(int arr[], int L, int R, int target) {//求最小的i,使得a[i] > target,若不存在,则返回-1
    while (L < R) {
        int m = L + ((R - L) >> 1);//向下取整
        if (arr[m] <= target)
            L = m + 1;
        else
            R = m;
    }
    if (arr[R] > target)
        return R;
    return -1;
}
int lastSmall(int arr[], int L, int R, int target) {//求最大的i,使得a[i] < target,若不存在,则返回-1
    while (L < R) {
        int m = L + ((R + 1 - L) >> 1);//向上取整
        if (arr[m] < target)
            L = m;
        else
            R = m - 1;
    }
    if (arr[L] < target)
        return L;
    return -1;
}
//kmp
void getNext(int *p,int next[]) {   //优化后的求next数组的方法 
    int len = m;
    next[0] = -1;    //next 数组中的 最大长度值(前后缀的公共最大长度) 的第一个 赋值为  -1  
    int k = -1,j = 0;
    while (j < len - 1) {
        if (k == -1 || p[j] == p[k]) { //p[k]表示前缀 p[j] 表示后缀
            k++; j++;
            if(p[j] != p[k])next[j] = k;
            else next[j] = next[k];   //因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k] = next[next[k]]
        }
        else k = next[k];
    }
}
int KMPSerach(int *s, int *p) {
    int sLen = n,pLen = m;
    int i = 0, j = 0;
    while (i < sLen && j < pLen) {
        if (j == -1 || s[i] == p[j])i++, j++;
        else j = nexts[j];
    }
    if (j == pLen)return i - j;
    else return -1;
}
//背包问题
//01背包
for(int i = 1;i <= n;i++){
    for(int j = V;j >= w[i];j--){
        dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
    }
}
//完全背包
for(int i = 0;i < n;i++){
    for(int j = w[i];j <= e-s;j++){
        dp[j] = min(dp[j],dp[j-w[i]]+v[i]);
    }
}
//多重背包
for(int i = 0;i < n;i++){
     for(int k = 0;k < b[i];k++){
         for(int j = n;j >= v[i];j--){
             dp[j] = max(dp[j],dp[j-v[i]]+w[i]);
         }
     }
}
//01背包 ,记录路径
const int N = 1e5 + 10;
int pre[N],dp[N],v[N],ans[N];
 
void p(int x){
    if(pre[x] == 0){
        cout<<ans[x];return;
    }
    p(pre[x]);
    cout<<' '<<ans[x];
}
int main(){
    ios_base::sync_with_stdio(0);
    int n,m;cin>>n>>m;
    for(int i = 0;i < n;i++)cin>>v[i];
    for(int i = 0;i < N;i++)pre[i] = -1,dp[i] = -INF;
    sort(v,v+n);
    dp[0] = 0; //和01背包类似,因为是恰好装满,其他只要赋上负无穷
    for(int i = 0;i < n;i++){
        for(int j = m;j >= v[i];j--){
            if(dp[j] <= dp[j-v[i]]+1){
                dp[j] = dp[j-v[i]]+1;
                ans[j] = v[i];
                pre[j] = j - v[i];
            }
        }
    }
    if(dp[m] <= 0) cout<<"No Solution";
    else p(m);
    cout<<endl;
    return 0;
}
//最大线段和
int maxsum(int n,int b[]){
	int sum = 0,temp = 0;
	for(int i = 0;i < n;i++)
	{
		temp>0?temp+=b[i]:temp=b[i];
		if(temp > sum)sum = temp;
	}
	return sum;
}
int maxsum2(int m,int n,int **a){
	int *b = new int[n],sum = 0;
	for(int i = 0;i < m;i++)
	{
		for(int k = 0;k < n;k++)
			b[k] = 0;
		for(int j = i;j < m;j++)
		{
			for(int k = 0;k < n;k++)
				b[k] += a[j][k];
			int temp = maxsum(n,b);
			if(temp > sum)sum = temp;
		}
	}
	return sum;
} 
//全排列(去重)
void permutation(int *arr,int *p,int n,int cur){
    if(cur == n){
        for(int i = 0; i < n; i++)
            printf("%d ",arr[i]);
        printf("\n");
    }else for(int i = 0; i < n; i++)if(!i || p[i] != p[i-1]){
        int c1 = 0, c2 = 0; 
        for(int j = 0; j < n; j++)
            if(p[j] == p[i]) // 重复元素的个数
                c1++;
        for(int j = 0; j < cur; j++)
            if(arr[j] == p[i]) // 前面已经排列的重复元素的个数 
                c2++;
        if(c2 < c1){
            arr[cur] = p[i];
            permutation(arr, p, n, cur+1);
        }
    }
} 
//全排列(函重)
void permutation(int arr[], int cur, int n){
    if( cur == n){
        for(int i = 0; i < n; i++)
            printf("%d ", arr[i]);
        printf("\n");
    }
    else for(int i = cur; i < n; i++){
        swap(arr[i], arr[cur]);
        permutation(arr, cur+1, n);
        swap(arr[i], arr[cur]);
    }
}
//n后排列
int vis[n],path[n],count = 0;
memset(path,0,sizeof(path));memset(vis,0,sizeof(vis));DFS(1);
void DFS(int tmp){//tmp目前排了多少皇后,x目前排的列,path具体排法 
	if (tmp == 9)
	{ //8个行均排满 
		count++;
		for (int i = 1; i <= 8; i++)
		{
			printf("%d ",path[i]);
		}
		printf("    %d\n",count);
		return;
	}
	for (int i = 1; i <= 8; i++)//8个列循环扫描 
	{
		if (!vis[i])
		{
			bool flag = false;
			for (int j = 1; j < tmp; j++)
			{
				if (tmp - i == j - path[j] || tmp + i == path[j] + j)
				{
					flag = true;
					break;
				}
			}
			if (flag) continue;
			vis[i] = true;
			path[tmp] = i;//标记位置
			DFS(tmp + 1);//下一行 
			vis[i] = false;
			path[tmp] = 0;
		}
	}
}
//并查集
int parent[maxn], rank[maxn];  //parent[]保存祖先,rank记录每个'树的高度'
void init(){
    for(int i = 0; i < maxn; i++)parent[i] = i; //注意这里
    for(int i = 0; i < maxn; i++)rank[i] = 1;
}
int findRoot(int v){
    while(parent[v] != v){
        parent[v] = parent[parent[v]]; // 路径压缩
        v = parent[v];
    }
    return v;
}
void unions(int a, int b){
    int aRoot = findRoot(a);
    int bRoot = findRoot(b);
    if (aRoot == bRoot)
        return;
    if (rank[aRoot] < rank[bRoot])
        parent[aRoot] = bRoot;
    else if(rank[aRoot] > rank[bRoot]){ 
        parent[bRoot] = aRoot;
    }else{ 
        parent[aRoot] = bRoot;
        rank[bRoot]++;
    }
}
int is_same(int x,int y){ //检查是不是在同一个集合中
    return findRoot(x) == findRoot(y);
}
//树状数组 
int lowbit(int x){//计算2^k
    return x&(-x);
}
void update(int i,int val){//更新数组的值
    while(i < maxn){               //注意这里是最大的x,没有记录所以用maxn,不能用n
        c[i] += val;
        i += lowbit(i);           //不断的往上面更新
    }
}
int sum(int i){//查询
    int s = 0;
    while(i > 0){
        s += c[i];
        i -= lowbit(i);           //不断的往下面加
    }
    return s;
}
//LCS 
int LCS(char *s1,char *s2){
    int len1 = strlen(s1)-1,len2 = strlen(s2)-1;//注意例如 abcfbc的strlen(s1)为7,所以是strlen(s1) - 1
    for(int i = 0; i <= len1; i++) dp[i][0] = 0;
    for(int i = 0; i <= len2; i++) dp[0][i] = 0;
    for(int i = 1; i <= len1; i++){
        for(int j = 1; j <= len2; j++)
            if(s1[i] == s2[j]){
                dp[i][j] = dp[i-1][j-1] + 1;
                path[i][j] = 1;
            }
            else if(dp[i-1][j] >= dp[i][j-1]) {
                dp[i][j] = dp[i-1][j];
                path[i][j] = 2;
            }
            else {
                dp[i][j] = dp[i][j-1];
                path[i][j] = 3;
            }
    }
    return dp[len1][len2];
}
//堆优化dijkstra
struct Node{
    int v,w;
    Node(int v,int w):v(v),w(w){}
    bool operator < (const Node&rhs) const {
        return rhs.w < w;
    }
};
vector<Node>G[maxn];
bool vis[maxn];
int d[maxn];
int n,m;
void init(){
    for(int i = 0; i < maxn; i++)G[i].clear();
    for(int i = 0; i < maxn; i++)vis[i] = false;
    for(int i = 0; i < maxn; i++)d[i] = INF;
}
int dijkstra(int s,int e){ //传入起点终点
    priority_queue<Node>q;
    q.push(Node(s,0));
    d[s] = 0;
    while(!q.empty()){
        Node now = q.top(); q.pop();
        int v = now.v;
        if(vis[v])continue;
        vis[v] = true;
        for(int i = 0; i < G[v].size(); i++){
            int v2 = G[v][i].v;
            int w = G[v][i].w;
            if(!vis[v2] && d[v2] > w+d[v]){
                d[v2] = w+d[v];
                q.push(Node(v2,d[v2]));
            }
        }
    }
    return d[e];
}
//用于最长非递减子序列种的lower_bound函数
int cmp(int a,int b){
    return a <= b;
}
//最长上升子序列
//dp[i]表示长度为i+1的上升子序列的最末尾元素 找到第一个比dp末尾大的来代替 
int LIS(int n){
    memset(dp, 0x3f, sizeof(dp));
    pos[0] = -1;
    int i,lpos;
    for (i = 0; i < n; ++i){
        dp[lpos = (lower_bound(dp, dp + n, a[i]) - dp)] = a[i];
        pos[lpos] = i;    // *靠后打印
        fa[i] = (lpos ? pos[lpos - 1] : -1);
    }
    n = lower_bound(dp, dp + n, INF) - dp;
    for (i = pos[n - 1]; ~fa[i]; i = fa[i]) ans.push_back(a[i]);
    ans.push_back(a[i]);
    return n;
}
//非递减的LIS
int LISS(int n){
    memset(dp, 0x3f, sizeof(dp));
    pos[0] = -1;
    int i,lpos;
    for (i = 0; i < n; i++){
        dp[lpos = (lower_bound(dp, dp + n, a[i],cmp) - dp)] = a[i]; //注意这里cmp
        pos[lpos] = i;    // *靠后打印
        fa[i] = (lpos ? pos[lpos - 1] : -1);
    }
    n = lower_bound(dp, dp + n, INF) - dp;
    for (i = pos[n - 1]; ~fa[i]; i = fa[i]) ans.push_back(a[i]);
    ans.push_back(a[i]);
    return n;
}
//单元最短路
const int maxn = 1e5 + 10;
vector<pair<int,int> > E[maxn];
int inq[maxn],n,m;
ll dis[maxn];
queue<int> q;
void spfa() {
    for(int i = 0;i < maxn;i++) {
        dis[i] = 2000000000;
        inq[i] = 0;
    }
    dis[1] = 0;
    q.push(1);
    while(!q.empty()) {
        int t = q.front();
        q.pop();inq[t] = 0;
        for(int i = 0;i < E[t].size();i++) {
            int to = E[t][i].first;
            ll di = E[t][i].second;
            if(dis[to] > dis[t] + di) {
                dis[to] = dis[t] + di;
                if(!inq[to]) {
                    inq[to] = 1;
                    q.push(to);
                }
            }
        }
    }
}
//SLF优化:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j) < dist(i),则将j插入队首,否则插入队尾 
void spfa() {
    for (int i = 0; i < maxn; i++) dis[i] = INF;
    deque<int> q;
    dis[s] = 0;
    q.push_back(s);
    while (!q.empty()) {
        int from = q.front();
        q.pop_front();
        inq[from] = 0;
        for (int i = 0; i < E[from].size(); i++) {
            int to = E[from][i].first;
            int di = E[from][i].second;
            if(dis[to] > dis[from] + di) {
                dis[to] = dis[from] + di;
                if(inq[to] == 0) {
                    inq[to] = 1;
                    if(q.size() && dis[to] < dis[from]) {
                        q.push_front(to);
                    } else {
                        q.push_back(to);
                    }
                }
            }
        }
    }
}
//线段树
const int MAXM=50000;          //定义 MAXM 为线段最大长度
int a[MAXM+5],st[(MAXM<<2)+5];    // a 数组为 main 函数中读入的内容,st 数组为需要查询的数的信息(如和、最值等),树的空间大小为线段最大长度的四倍
void build(int o,int l,int r){    //传入的参数为 o:当前需要建立的结点;l:当前需要建立的左端点;r:当前需要建立的右端点
    if(l==r)st[o]=a[l];      //当左端点等于右端点即建立叶子结点时,直接给数组信息赋值
    else{
        int m=l+((r-l)>>1);      // m 为中间点,左儿子结点为 [l,m] ,右儿子结点为 [m+1,r];
        build(o<<1,l,m);        //构建左儿子结点
        build((o<<1)|1,m+1,r);     //构建右儿子结点
        st[o]=st[o<<1]+st[(o<<1)|1];  //递归返回时用儿子结点更新父节点,此处可进行更新最大值、最小值、区间和等操作
    }
}                      //在 main 函数中的语句  build(1,1,n);
//单点修改
void update(int o,int l,int r,int ind,int ans){  //o、l、r为当前更新到的结点、左右端点,ind为需要修改的叶子结点左端点,ans为需要修改成的值;
    if(l==r){                      //若当前更新点的左右端点相等即到叶子结点时,直接更新信息并返回
        st[o]=ans;
        return;
    }
    int m=l+((r-l)>>1);
    if(ind<=m){                      //若需要更新的叶子结点在当前结点的左儿子结点的范围内,则递归更新左儿子结点,否则更新右儿子结点
        update(o<<1,l,m,ind,ans);
    }
    else{
        update((o<<1)|1,m+1,r,ind,ans);
    }
    st[o]=max(st[o<<1],st[(o<<1)|1]);//递归回之后用儿子结点更新父节点(此处是区间最大值)
}          //在main函数中的语句 update(1,1,n,ind,ans);
//对应单点修改的区间查询
   int query(int o,int l,int r,int ql,int qr){      //ql、qr为需要查询的区间左右端点
    if(ql>r||qr<l) return -1;              //若当前结点和需要查找的区间不相交,则返回一个对于区间查询无关的值(如求和时返回0,求最大值时返回-1等)
    if(ql<=l&&qr>=r) return st[o];        //若当前结点的区间被需要查询的区间覆盖,则返回当前结点的信息
    int m=l+((r-l)>>1);
    int p1=query(o<<1,l,m,ql,qr),p2=query((o<<1)|1,m+1,r,ql,qr);  //p1为查询左儿子结点得到的信息,p2为查询右儿子结点得到的信息
    return max(p1,p2);    //综合两个儿子结点的信息并返回
}//main函数中的语句 printf("%d\n",query(1,1,n,a,b));
//区间加值
void pushup(int o){          //pushup函数,该函数本身是将当前结点用左右子节点的信息更新,此处求区间和,用于update中将结点信息传递完返回后更新父节点
    st[o]=st[o<<1]+st[o<<1|1];
} 
void pushdown(int o,int l,int r){  //pushdown函数,将o结点的信息传递到左右子节点上
    if(add[o]){             //当父节点有更新信息时才向下传递信息
        add[o<<1]+=add[o];      //左右儿子结点均加上父节点的更新值
        add[o<<1|1]+=add[o];
        int m=l+((r-l)>>1);
        st[o<<1]+=add[o]*(m-l+1);  //左右儿子结点均按照需要加的值总和更新结点信息
        st[o<<1|1]+=add[o]*(r-m);
        add[o]=0;                //信息传递完之后就可以将父节点的更新信息删除
    }
}
void update(int o,int l,int r,int ql,int qr,int addv){  //ql、qr为需要更新的区间左右端点,addv为需要增加的值
    if(ql<=l&&qr>=r){                      //与单点更新一样,当当前结点被需要更新的区间覆盖时
        add[o]+=addv;                      //更新该结点的所需更新信息
        st[o]+=addv*(r-l+1);                //更新该结点信息
        return;                    //根据lazy思想,由于不需要遍历到下层结点,因此不需要继续向下更新,直接返回
    }
    
    pushdown(o,l,r);                  //将当前结点的所需更新信息传递到下一层(其左右儿子结点)
    int m=l+((r-l)>>1);
    if(ql<=m)update(o<<1,l,m,ql,qr,addv);     //当需更新区间在当前结点的左儿子结点内,则更新左儿子结点
    if(qr>=m+1)update(o<<1|1,m+1,r,ql,qr,addv);   //当需更新区间在当前结点的右儿子结点内,则更新右儿子结点
    pushup(o);                  //递归回上层时一步一步更新回父节点
}
ll query(int o,int l,int r,int ql,int qr){    //ql、qr为需要查询的区间
    if(ql<=l&&qr>=r) return st[o];      //若当前结点覆盖区间即为需要查询的区间,则直接返回当前结点的信息
    pushdown(o,l,r);                  //将当前结点的更新信息传递给其左右子节点
    int m=l+((r-l)>>1);
    ll ans=0;                      //所需查询的结果
    if(ql<=m)ans+=query(o<<1,l,m,ql,qr);     //若所需查询的区间与当前结点的左子节点有交集,则结果加上查询其左子节点的结果
    if(qr>=m+1)ans+=query(o<<1|1,m+1,r,ql,qr); //若所需查询的区间与当前结点的右子节点有交集,则结果加上查询其右子节点的结果
   return ans; 
}
//区间改值(其实只有pushdow函数和update中修改部分与区间加值不同)
void pushup(int o){
     st[o]=st[o<<1]+st[o<<1|1];
 }
 void pushdown(int o,int l,int r){  //pushdown和区间加值不同,改值时修改结点信息只需要对修改后的信息求和即可,不用加上原信息
     if(change[o]){
         int c=change[o];
         change[o<<1]=c;
         change[o<<1|1]=c;
         int m=l+((r-l)>>1);
         st[o<<1]=(m-l+1)*c;
         st[o<<1|1]=(r-m)*c;
         change[o]=0;
     }
 }
 void update(int o,int l,int r,int ql,int qr,int c){
     if(ql<=l&&qr>=r){         //同样更新结点信息和区间加值不同
         change[o]=c;
         st[o]=(r-l+1)*c;
         return;
     }
     
     pushdown(o,l,r);
     int m=l+((r-l)>>1);
     if(ql<=m)update(o<<1,l,m,ql,qr,c);
     if(qr>=m+1)update(o<<1|1,m+1,r,ql,qr,c);
     pushup(o);
 }
 int query(int o,int l,int r,int ql,int qr){
     if(ql<=l&&qr>=r) return st[o];
     pushdown(o,l,r);
     int m=l+((r-l)>>1);
     int ans=0;
     if(ql<=m)ans+=query(o<<1,l,m,ql,qr);
     if(qr>=m+1)ans+=query(o<<1|1,m+1,r,ql,qr);
     return ans;
 }
//Trie树 
const int maxn = 1e6 + 10;
char S[maxn];
 
struct Trie{
    int next[26];
    int cnt;
    void init() {
        cnt = 0;
        memset(next, -1, sizeof next);
    }
}T[maxn];
int le;
 
void Insert(string s) {
    int p = 0;
    for (int i = 0; i < s.size(); i++) {
        int r = s[i] - 'a';
        if(T[p].next[r] == -1) {
            T[le].init();
            T[p].next[r] = le++;
        }
        p = T[p].next[r];
        T[p].cnt++;
 
    }
}
 
void query(string s) {
    int p = 0;
    for (int i = 0; i < s.size(); i++) {
        int r = s[i] - 'a';
        if(T[p].next[r] == -1) {
            cout<<0<<endl;
            return ;
        }
        p = T[p].next[r];
    }
    cout<<T[p].cnt<<endl;
}
 
int main() {
    ios_base::sync_with_stdio(0);
    int n;cin>>n;
    T[0].init();
    le = 1;
    for (int i = 0; i < n; i++) {
        string s;cin>>s;
        Insert(s);
    }
    int m;cin>>m;
    while (m--) {
        string s;cin>>s;
        query(s);
    }
    return 0;
} 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值