• 长乐集训集合


    一些结论题和码农题不在此列。
    赛时切的也不列入。

    #504. 「插头 DP」方格填写

    \(f(x)^2\)看作两张图,其最后状态一样的方案数。

    直接对其进行插头dp计算这种方案数即可,考虑按前缀贡献进行分类讨论刷表转移。

    方格填写
    // code by fhq_treap
    #include<bits/stdc++.h>
    #define ll long long
    #define N 20005
    #define mod 998244353
    
    inline ll read(){
        char C=getchar();
        ll A=0 , F=1;
        while(('0' > C || C > '9') && (C != '-')) C=getchar();
        if(C == '-') F=-1 , C=getchar();
        while('0' <= C && C <= '9') A=(A << 1)+(A << 3)+(C - 48) , C=getchar();
        return A*F;
    }
    
    template <typename T>
    void write(T x)
    {
        if(x < 0) {
            putchar('-');
            x = -x;
        }
        if(x > 9)
            write(x/10);
        putchar(x % 10 + '0');
        return;
    }
    
    int n,m;
    
    ll f[2][10][(1 << 8)][(1 << 8)];
    
    int a[100][20];
    
    int c[4];
    
    inline void del(int &x,int L,int k,int w){
    	x = L;
    	if(x & (1 << m))
    	x ^= (1 << m);
    	if(k == 0)
    	if(x & (1 << (w - 1)))
    	x ^= (1 << (w - 1));
    	if(k == 1){
    	if(x & (1 << (w - 1)))
    	x ^= (1 << (w - 1));
    	x |= (1 << m);
    	}
    	if(k == 2)
    	x |= (1 << (w - 1));
    	if(k == 3){
    	x |= (1 << (w - 1));
    	x |= (1 << m);
    	}
    }
    
    inline void print(int x){
    	for(int i = 0;i <= m;++i)
    	std::cout<<((x & (1 << i)) > 0)<<" ";
    	puts("");
    }
    
    int main(){
    	c[0] = 0,c[1] = 1,c[2] = 1,c[3] = 2;
    	freopen("grid.in","r",stdin);
    	freopen("grid.out","w",stdout);
    	int T;
    	scanf("%d",&T);
    	while(T -- ){
    	memset(f,0,sizeof(f));
    	n = read(),m = read();
    	for(int i = 1;i <= n;++i)
    	for(int j = 1;j <= m;++j)
    	a[i][j] = read();
    	f[0][m][0][0] = 1;
    	for(int i = 1;i <= n;++i)
    	for(int j = 1;j <= m;++j){
            int now = i & 1;//现在的表
            int lasx = i & 1;
            int lasy = j - 1;
            if(lasy == 0)lasy = m,lasx ^= 1;
    //        std::cout<<"("<<i<<","<<j<<")"<<"'s las "<<"("<<lasx<<","<<lasy<<")"<<std::endl;
            for(int lasA = 0;lasA < (1 << (m + 1));++lasA)
            for(int lasB = 0;lasB < (1 << (m + 1));++lasB){//A B两个图的表
    			if(f[lasx][lasy][lasA][lasB]){
    //			puts("LASA");
    //			print(lasA);
    //			puts("LASB");
    //			print(lasB);
    			int cA = ((lasA & (1 << m)) > 0) + ((lasA & (1 << (j - 1))) > 0);
    			int cB = ((lasB & (1 << m)) > 0) + ((lasB & (1 << (j - 1))) > 0);//前面的度数
    //			std::cout<<"lasCNT"<<" "<<cA<<" "<<cB<<std::endl;
    //			std::cout<<"THE CNT OF "<<f[lasx][lasy][lasA][lasB]<<std::endl;
    			for(int A = 0;A < 4;++A)
    			for(int B = 0;B < 4;++B){
    				if(j == m && ((A != 0 && A != 2) || (B != 0 && B != 2)))
    				continue;
    				if(i == n && ((A != 0 && A != 1) || (B != 0 && B != 1)))
    				continue;
    //				std::cout<<"TRY "<<A<<" "<<B<<" "<<cA + c[A]<<" "<<cB + c[B]<<std::endl;
    				if(cA + c[A] == cB + c[B]){
    					if(cA + c[A] != a[i][j] && a[i][j] != -1)
    					continue;
    					if(cB + c[B] != a[i][j] && a[i][j] != -1)
    					continue;
    					int tA,tB;
    //                    std::cout<<"USE "<<A<<" "<<B<<std::endl;
    					del(tA,lasA,A,j);
    					del(tB,lasB,B,j);
    //					print(tA);
    //                    print(tB);
    //                    puts("");
    					f[now][j][tA][tB] = (f[now][j][tA][tB] + f[lasx][lasy][lasA][lasB]);
    					if(f[now][j][tA][tB] > mod)
    					f[now][j][tA][tB] -= mod;
    				}
    			}
    //			puts("");
    //			puts("");
    			}
    		}
    //		std::cout<<"ready to print the ans"<<std::endl;
    //		for(int lasA = 0;lasA < (1 << (m + 1));++lasA)
    //        for(int lasB = 0;lasB < (1 << (m + 1));++lasB){
    //        	if(!f[now][j][lasA][lasB])continue;
    //            puts("A");
    //            print(lasA);
    //            puts("B");
    //            print(lasB);
    //			std::cout<<f[now][j][lasA][lasB]<<std::endl;
    //			puts("");
    //		}
    		for(int lasA = 0;lasA < (1 << (m + 1));++lasA)
            for(int lasB = 0;lasB < (1 << (m + 1));++lasB)
            f[now ^ 1][j][lasA][lasB] = 0;
    	}
    	std::cout<<f[n & 1][m][0][0]<<std::endl;
    	}
    }
    
    
    

    #535. 「后缀数组」相似子串

    考虑这类对子状物相同的判断,我们直接记录最小子状物的状态。

    本题即记录前缀相同最近的距离即可。

    然后每种的第一个使用\(-1\)代替。

    考虑对这些后缀构成的串进行排序然后计算LCP即可解决原题。

    思考如何进行快速排序。

    有两类思路:

    使用数据结构,我们可以使用可持久化分块数组即可。

    利用字符集大小,字符集大小只有10,我们直接按-1的位置分类,比较两两的对。

    「后缀数组」相似子串
    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Rg register
    #define RI Rg int
    #define Cn const
    #define CI Cn int&
    #define I inline
    #define W while
    #define N 100000
    #define LG 17
    using namespace std;
    int n,s[N+5],lst[10];char st[N+5];
    namespace SA
    {
    	int SA[N+5],rk[N+5],p[N+5],t[N+5];I void Sort(CI S)
    	{
    		RI i;for(i=0;i<=S;++i) t[i]=0;for(i=1;i<=n;++i) ++t[rk[i]];
    		for(i=1;i<=S;++i) t[i]+=t[i-1];for(i=n;i;--i) SA[t[rk[p[i]]]--]=p[i];
    	}
    	I void GetSA()
    	{
    		RI i,k,t=0,S=n;for(i=1;i<=n;++i) rk[p[i]=i]=s[i];for(Sort(S),k=1;t^n;k<<=1,S=t)
    		{
    			for(t=0,i=1;i<=k;++i) p[++t]=n-k+i;for(i=1;i<=n;++i) SA[i]>k&&(p[++t]=SA[i]-k);for(Sort(S),i=1;i<=n;++i) p[i]=rk[i];
    			for(rk[SA[1]]=t=1,i=2;i<=n;++i) (p[SA[i]]^p[SA[i-1]]||p[SA[i]+k]^p[SA[i-1]+k])&&++t,rk[SA[i]]=t;
    		}
    	}
    	int H[N+5],Lg[N+5],Mn[N+5][LG+1];I void GetH()
    	{
    		RI i,j,k=0;for(i=1;i<=n;++i) rk[SA[i]]=i;
    		for(i=1;i<=n;++i) if(k&&--k,rk[i]^1) {j=SA[rk[i]-1];W(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) ++k;H[rk[i]]=k;}
    		for(Lg[0]=-1,i=2;i<=n;++i) Lg[i]=Lg[i>>1]+1,Mn[i][0]=H[i];
    		for(j=1;(1<<j)<=n;++j) for(i=2;i+(1<<j)-1<=n;++i) Mn[i][j]=min(Mn[i][j-1],Mn[i+(1<<j-1)][j-1]);		
    	}
    	I int LCP(CI x,CI y)
    	{
    		if(x==y) return 1e9;RI l=rk[x],r=rk[y];l>r&&(swap(l,r),0),++l;
    		RI k=Lg[r-l+1];return min(Mn[l][k],Mn[r-(1<<k)+1][k]);
    	}
    }
    namespace SS
    {
    	int nxt[N+5][10],ct[N+5],q[N+5][11],lst[N+5],id[N+5],pos[N+5];
    	I int Calc(CI x,CI y)
    	{
    		RI i=1,ans=0,wx=x,wy=y,nx,ny,t,o;W(wx<=n&&wy<=n)
    		{
    			nx=i<=ct[x]?q[x][i]:n+1,ny=i<=ct[y]?q[y][i]:n+1,t=min(nx-wx,ny-wy);if((o=SA::LCP(wx,wy))<t) return ans+o;
    			if(nx^(wx+t)||ny^(wy+t)) return ans+t;ans+=t;if(nx>n||ny>n) return ans;++ans,wx=nx+1,wy=ny+1,++i;
    		}return ans;
    	}
    	I bool cmp(CI x,CI y)
    	{
    		RI i,t=Calc(x,y),vx=x+t<=n?s[x+t]:-1,vy=y+t<=n?s[y+t]:-1;
    		for(i=1;i<=ct[x];++i) q[x][i]==x+t&&(vx=0);for(i=1;i<=ct[y];++i) q[y][i]==y+t&&(vy=0);return vx<vy;
    	}
    	int Lg[N+5],Mn[N+5][LG+1];I void Build()
    	{
    		RI i,j;for(i=n;i;--i)
    		{
    			for(j=0;j<=9;++j) nxt[i][j]=nxt[i+1][j];nxt[i][st[i]&15]=i;
    			for(j=0;j<=9;++j) nxt[i][j]&&(q[i][++ct[i]]=nxt[i][j]);sort(q[i]+1,q[i]+ct[i]+1);
    		}
    		for(i=1;i<=n;++i) id[i]=i;for(stable_sort(id+1,id+n+1,cmp),i=1;i<=n;++i) pos[id[i]]=i;
    		for(Lg[0]=-1,i=2;i<=n;++i) Lg[i]=Lg[i>>1]+1,Mn[i][0]=Calc(id[i-1],id[i]);
    		for(j=1;(1<<j)<=n;++j) for(i=2;i+(1<<j)-1<=n;++i) Mn[i][j]=min(Mn[i][j-1],Mn[i+(1<<j-1)][j-1]);
    	}
    	I int Q(CI l,CI r) {if(l>r) return 1e9;RI k=Lg[r-l+1];return min(Mn[l][k],Mn[r-(1<<k)+1][k]);}
    	I int Qry(RI x,CI s)
    	{
    		RI l,r,mid;x=pos[x];
    		l=1,r=x;W(l^r) mid=l+r-1>>1,Q(mid+1,x)>=s?r=mid:l=mid+1;RI o=r;
    		l=x,r=n;W(l^r) mid=l+r+1>>1,Q(x+1,mid)>=s?l=mid:r=mid-1;return l-o+1;
    	}
    }
    int main()
    {
    	freopen("similar.in","r",stdin),freopen("similar.out","w",stdout); 
    	RI Qt,i;for(scanf("%d%d%s",&n,&Qt,st+1),i=1;i<=n;++i) s[i]=lst[st[i]&15]?i-lst[st[i]&15]:0,lst[st[i]&15]=i;
    	SA::GetSA(),SA::GetH(),SS::Build();
    	RI x,y,lst=0;W(Qt--) scanf("%d%d",&x,&y),x^=lst,y^=lst,printf("%d\n",lst=SS::Qry(x,y-x+1));return 0;
    }
    
    

    #825. 「计算几何初探」三角查找

    考虑枚举底如何快速计算高是否有。

    我们把这条边旋转为y轴,并考虑按x的偏序二分。

    我们发现按x偏序的话,两点偏序关系只与我们枚举的底的斜率和两点构成的斜率有关。

    对斜率排序,发现我们按大小枚举斜率时,只有枚举的这条边的两点偏序关系改变,把他们两个交换即可。

    「计算几何初探」三角查找
    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    #define N 5005
    struct hehe{
    	int x, y;
    }a[N];
    struct haha{
    	int a, b;
    	hehe p;
    }e[N * N];
    int pos[N], rk[N];
    bool cmp1(hehe x, hehe y)
    {
    	return x.x == y.x ? x.y < y.y : x.x < y.x;
    }
    bool cmp2(haha x, haha y)
    {
    	return x.p.x * y.p.y - x.p.y * y.p.x > 0;
    }
    hehe xl(hehe x, hehe y)
    {
    	hehe ret;
    	ret.x = x.x - y.x;
    	ret.y = x.y - y.y;
    	return ret;
    }
    int cj(hehe x, hehe y)
    {
    	return x.x * y.y - x.y * y.x;
    }
    signed main()
    {
    	freopen("triangle.in","r",stdin);
    	freopen("triangle.out","w",stdout);
    	int n, cnt = 0, s;
    	cin >> n >> s;
    	for(int i = 1; i <= n; i++)
    	{
    		cin >> a[i].x >> a[i].y;
    	}
    	sort(a + 1, a + n + 1, cmp1);
    	s *= 2;
    	for(int i = 1; i <= n; i++)
    	{
    		for(int j = 1; j < i; j++)
    		{
    			hehe qwq;
    			qwq.x = a[i].x - a[j].x;
    			qwq.y = a[i].y - a[j].y;
    			e[++cnt].a = j;
    			e[cnt].b = i;
    			e[cnt].p = qwq;
    		}
    	}
    	sort(e + 1, e + cnt + 1, cmp2);
    	for(int i = 1; i <= n; i++) rk[i] = pos[i] = i;
    	for(int i = 1; i <= cnt; i++)
    	{
    		hehe p = e[i].p;
    		int x = e[i].a, y = e[i].b;
    		if(rk[x] > rk[y]) swap(x, y);
    		int l = 1, r = rk[x] - 1;
    		while(l <= r)
    		{
    			int mid = (l + r) >> 1;
    			int si = abs(cj(p, xl(a[pos[mid]], a[pos[rk[x]]])));
    			if(si == s)
    			{
    				cout << "Yes" << endl << a[x].x << ' ' << a[x].y << endl << a[y].x << ' ' << a[y].y << endl << a[pos[mid]].x << ' ' << a[pos[mid]].y;
    				exit(0);
    			}
    			else if(si > s) l = mid + 1;
    			else r = mid - 1;
    		}
    		swap(rk[x], rk[y]);
    		swap(pos[rk[x]], pos[rk[y]]);
    	}
    	cout << "No" << endl;
    	return 0;
    }
    

    #981. 「prufer编码」森林之和

    考虑先思考如何计数一颗树的贡献。

    我们发现在其为无根树,我们不妨为其确定根,则所有点只在作为根时贡献,其贡献和相等。

    那么我们不妨强制\(1\)为根。

    考虑如何确定其。

    我们设\(f_{i,j}\)\(1\)的儿子为\(j\)个的整体树大小为\(i\)的树方案。

    考虑\(purfer\)序列,其1的度为\(j\),那么我们选取\(j - 1\)个位置,剩下随便填即可。

    \(f_{i,j} = \binom{i - 2}{j - 1} * (i - j - 1) ^ {i - 1}\)

    \(g_i\)为大小为\(i\)的树的所有贡献。

    那么有\(g_i = i * \sum_{c = 1} f_{i,c} * c^2\)

    再考虑回到原问题,我们强制枚举森林里的块大小,再算贡献

    那么有\(ans = \sum_{i = 1} g_i * S(n - i)\)

    其中\(S(n)\)\(n\)个点组成的森林大小。

    枚举1所在的联通块那么有\(S(n) = \sum_i \binom{n}{i - 1} * (i) ^ {i - 2} * S(n - i)\)

    「prufer编码」森林之和
    // code by fhq_treap
    #include <bits/stdc++.h>
    #define ll long long
    #define N 6005
    
    inline ll read() {
        char C = getchar();
        ll A = 0, F = 1;
        while (('0' > C || C > '9') && (C != '-')) C = getchar();
        if (C == '-')
            F = -1, C = getchar();
        while ('0' <= C && C <= '9') A = (A << 1) + (A << 3) + (C - 48), C = getchar();
        return A * F;
    }
    
    template <typename T>
    void write(T x) {
        if (x < 0) {
            putchar('-');
            x = -x;
        }
        if (x > 9)
            write(x / 10);
        putchar(x % 10 + '0');
        return;
    }
    
    int mod;
    
    inline ll qpow(ll a, ll b) {
        ll ans = 1;
        while (b) {
            if (b & 1)
                ans = ans * a % mod;
            a = a * a % mod;
            b >>= 1;
        }
        return ans;
    }
    
    int s[N], inv[N];
    
    int f[N][N];  //按照1~n编号加入的顺序,1号点的度数为x的方案数
    
    int g[N];
    
    int q[N];
    
    int p[N][N];
    
    int n = 5e3;
    
    int T;
    
    int c[N][N];
    
    inline ll C(int x, int y) {
        if (c[x][y])
            return c[x][y];
        return c[x][y] = 1ll * s[x] * inv[y] % mod * inv[x - y] % mod;
    }
    
    int Ans[N];
    
    int main() {
        freopen("forest.in", "r", stdin);
        freopen("forest.out", "w", stdout);
        scanf("%d", &T);
        scanf("%d", &mod);
    
        s[0] = 1;
        for (int i = 1; i <= n; ++i) s[i] = 1ll * s[i - 1] * i % mod;
    
        inv[n] = qpow(s[n], mod - 2);
    
        for (int i = 1; i <= n; ++i) {
            p[i][0] = 1;
            for (int j = 1; j <= n; ++j) {
                p[i][j] = 1ll * p[i][j - 1] * i % mod;
            }
        }
    
        for (int i = n - 1; i >= 0; --i) {
            inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
            //		std::cout<<s[i]<<" "<<inv[i]<<" "<<1ll * inv[i] * s[i] % mod<<std::endl;
        }
    
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j < i; ++j) {
                f[i][j] = (1ll * C(i - 2, j - 1) % mod * p[i - 1][i - j - 1]) % mod;
                //		std::cout<<i<<" "<<j<<" "<<f[i][j]<<std::endl;
            }
    
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= i - 1; ++j) {
                g[i] = (g[i] + 1ll * f[i][j] * j % mod * j % mod);  // g_i : i点1的贡献
                if (g[i] > mod)
                    g[i] -= mod;
            }
        }
    
        for (int i = 1; i <= n; ++i) g[i] = (1ll * g[i] % mod * i) % mod;
    
        q[0] = 1;
    
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= i; ++j) {
                q[i] = (q[i] + 1ll * C(i - 1, j - 1) * (j - 2 < 0 ? 1 : p[j][j - 2]) % mod * q[i - j] % mod);
                if (q[i] > mod)
                    q[i] -= mod;
                //			std::cout<<j<<"->"<<i<<" "<<(i - 2 < 0 ? 1 : p[i][i - 2])<<"
                //"<<q[i]<<std::endl;
            }
            //		std::cout<<i<<" "<<q[i]<<std::endl;
        }
    
        while (T--) {
            n = read();
            if (Ans[n]) {
                write(Ans[n]);
                puts("");
                break;
            }
            ll ans = 0;
            for (int i = 1; i <= n; ++i) {
                ans = (ans + 1ll * g[i] * C(n, i) % mod * q[n - i] % mod) % mod;
            }
            write(Ans[n] = ans);
            puts("");
        }
    }
    
    /*
    1000000007
    */
    
    

    #584. 「网络流」欧拉回路

    考虑二分答案。

    那么有一些边被唯一定向,有一些边没有。

    考虑如何对其判断是否有欧拉回路。

    不妨先对其任意定向,然后考虑如何调整。

    考虑设\(\delta_i\)为入度减出度。

    由于可以翻转边\(\delta_x + 2,\delta_y - 2\)

    考虑使用网络流解决其。

    我们从原点往每个\(\delta_i > 0\)的点连一条\(\frac{\delta_i}{2}\)的边,\(\delta_i < 0\)汇点连一条\(\frac{|\delta_i|}{2}\)的边,可翻转的边\(y \to x\ in\ [1]\)

    是否有欧拉回路即看是否流量满流。

    「网络流」欧拉回路
    #include <bits/stdc++.h>
    #include <bits/extc++.h>
    #define ll long long
    #define ull unsigned ll
    #define lowbit(x) (x & (-x))
    template <typename T>
    inline void read(T &x)
    {
        x = 0;
        char s = (char)getchar();
        bool f = false;
        while (!(s >= '0' && s <= '9'))
        {
            if (s == '-')
                f = true;
            s = (char)getchar();
        }
        while (s >= '0' && s <= '9')
        {
            x = (x << 1) + (x << 3) + s - '0';
            s = (char)getchar();
        }
        if (f)
            x = (~x) + 1;
    }
    template <typename T, typename... T1>
    inline void read(T &x, T1 &...x1)
    {
        read(x);
        read(x1...);
    }
    template <typename T>
    inline void ckmin(T &x, T y)
    {
        if (x > y)
            x = y;
    }
    template <typename T>
    inline void ckmax(T &x, T y)
    {
        if (x < y)
            x = y;
    }
    using namespace std;
    const int N = 5e4 + 5, M = 1e5 + 5;
    int n, m;
    struct node
    {
        int u, v, w1, w2;
    } e[N];
    struct Edge
    {
        int next, to, cap, flow;
    } edge[N];
    int head[N], num_edge = 1;
    inline void add_edge(int from, int to, int cap, bool flag = true)
    {
        edge[++num_edge].next = head[from];
        edge[num_edge].to = to;
        edge[num_edge].cap = cap;
        edge[num_edge].flow = 0;
        head[from] = num_edge;
        if (flag)
            add_edge(to, from, 0, false);
    }
    int dis[N], cur[N];
    int S, T;
    inline bool bfs()
    {
        memcpy(cur, head, sizeof(cur));
        memset(dis, 0, sizeof(dis));
        queue<int> q;
        q.push(S);
        dis[S] = 1;
        while (!q.empty())
        {
            int u = q.front();
            q.pop();
            for (int i = head[u]; i; i = edge[i].next)
            {
                int &v = edge[i].to;
                if (!dis[v] && edge[i].cap > edge[i].flow)
                {
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[T];
    }
    inline int dinic(int u, int flow)
    {
        if (u == T)
            return flow;
        int res = 0;
        for (int &i = cur[u]; i; i = edge[i].next)
        {
            int &v = edge[i].to;
            if (dis[v] == dis[u] + 1 && edge[i].cap > edge[i].flow)
            {
                int f = dinic(v, min(flow, edge[i].cap - edge[i].flow));
                if (f)
                {
                    res += f;
                    flow -= f;
                    edge[i].flow += f;
                    edge[i ^ 1].flow -= f;
                    if (!flow)
                        break;
                }
            }
        }
        return res;
    }
    inline int maxflow()
    {
        int res = 0;
        while (bfs())
            res += dinic(S, INT_MAX);
        return res;
    }
    int deg[N];
    inline bool check(int val)
    {
        num_edge = 1;
        memset(head, 0, sizeof(head));
        memset(deg, 0, sizeof(deg));
        for (int i = 1; i <= m; ++i)
        {
            if (e[i].w1 <= val)
            {
                ++deg[e[i].u];
                --deg[e[i].v];
                if (e[i].w2 <= val)
                    add_edge(e[i].u, e[i].v, 1);
            }
        }
        int sum = 0;
        for (int i = 1; i <= n; ++i)
        {
            if (deg[i] & 1)
                return false;
            deg[i] /= 2;
            if (deg[i] > 0)
            {
                add_edge(S, i, deg[i]);
                sum += deg[i];
            }
            else if (deg[i] < 0)
                add_edge(i, T, -deg[i]);
        }
        return sum == maxflow();
    }
    int id[N];
    list<pair<int, int>> g[N];
    inline void print(int u)
    {
        while (!g[u].empty())
        {
            int v = g[u].back().first, w = g[u].back().second;
            g[u].pop_back();
            print(v);
            printf("%d ", w);
        }
    }
    inline void solve(int val)
    {
        num_edge = 1;
        memset(head, 0, sizeof(head));
        memset(deg, 0, sizeof(deg));
        for (int i = 1; i <= m; ++i)
        {
            if (e[i].w1 <= val)
            {
                ++deg[e[i].u];
                --deg[e[i].v];
                if (e[i].w2 <= val)
                {
                    add_edge(e[i].u, e[i].v, 1);
                    id[i] = num_edge - 1;
                }
                else
                    id[i] = -1;
            }
        }
        int sum = 0;
        for (int i = 1; i <= n; ++i)
        {
            deg[i] /= 2;
            if (deg[i] > 0)
            {
                add_edge(S, i, deg[i]);
                sum += deg[i];
            }
            else if (deg[i] < 0)
                add_edge(i, T, -deg[i]);
        }
        maxflow();
        for (int i = 1; i <= m; ++i)
            if (id[i] >= 0 && edge[id[i]].flow == edge[id[i]].cap)
                g[e[i].u].push_back(make_pair(e[i].v, i));
            else
                g[e[i].v].push_back(make_pair(e[i].u, i));
        print(1);
        putchar('\n');
    }
    signed main()
    {
        freopen("euler.in", "r", stdin);
        freopen("euler.out", "w", stdout);
        read(n, m);
        S = 0, T = n + 1;
        int minn = 0, maxx = 0;
        for (int i = 1; i <= m; ++i)
        {
            read(e[i].u, e[i].v, e[i].w1, e[i].w2);
            if (e[i].w1 > e[i].w2)
            {
                swap(e[i].u, e[i].v);
                swap(e[i].w1, e[i].w2);
            }
            ++deg[e[i].u];
            ++deg[e[i].v];
            ckmax(minn, min(e[i].w1, e[i].w2));
            ckmax(maxx, max(e[i].w1, e[i].w2));
        }
        for (int i = 1; i <= n; ++i)
            if (deg[i] & 1)
            {
                printf("NIE\n");
                return 0;
            }
        memset(deg, 0, sizeof(deg));
        int l = minn, r = maxx, ans = 0;
        while (l <= r)
        {
            int mid = (l + r) >> 1;
            if (check(mid))
            {
                ans = mid;
                r = mid - 1;
            }
            else
                l = mid + 1;
        }
        printf("%d\n", ans);
        solve(ans);
        return 0;
    }
    
    

    #474. 「决策单调性优化 DP」网格选点

    考场上思考如何对每一层求出单点所练成的最小的矩形。

    赛后发现dp具有决策单调性,具体证明过程不在此赘述,可以形象理解,考虑当\(\delta x\)增大时其决策的位置一定会往后移动,因为此时\(\delta y\)所产生的贡献影响增大,又因为随着\(x\)增大其\(y\)在减小。

    考虑如何处点坐标的偏序关系,可以使用线段树分治即可。

    「决策单调性优化 DP」网格选点
    // code by fhq_treap
    #include <bits/stdc++.h>
    #define ll long long
    #define N 1000005
    
    inline ll read() {
        char C = getchar();
        ll A = 0, F = 1;
        while (('0' > C || C > '9') && (C != '-')) C = getchar();
        if (C == '-')
            F = -1, C = getchar();
        while ('0' <= C && C <= '9') A = (A << 1) + (A << 3) + (C - 48), C = getchar();
        return A * F;
    }
    
    template <typename T>
    void write(T x) {
        if (x < 0) {
            putchar('-');
            x = -x;
        }
        if (x > 9)
            write(x / 10);
        putchar(x % 10 + '0');
        return;
    }
    
    int n;
    
    struct Point {
        int x, y, ans;
    } P[N];
    
    bool operator<(Point a, Point b) { return a.x < b.x; }
    
    int T[N];  // BIT
    
    #define MAXN 1000000
    #define lowbit(x) (x & -x)
    
    inline void add(int x, int p) {
        x = std::max(1, x);
        for (int i = x; i <= N; i += lowbit(i)) T[i] = std::max(T[i], p);
    }
    
    inline int find(int x) {
    	if(x == 0)
    	return 1;
        if (x <= 0)
            return 0;
        int ans = 0;
        for (int i = x; i; i -= lowbit(i)) {
            ans = std::max(ans, T[i]);
        }
        return ans;
    }
    
    int t;
    
    using std::vector;
    
    vector<int> v[N];  //第i层。
    
    vector<int> need[N];  //处理的点
    
    inline bool in(int li, int ri) {  //? li \to ri
        return (P[li].x < P[ri].x && P[li].y < P[ri].y);
    }
    
    inline ll S(int a, int b) {
    //    std::cout<<"SSS "<<P[a].x<<" "<<P[a].y<<" "<<P[b].x<<" "<<P[b].y<<" "<<1ll * (P[a].x - P[b].x) * (P[a].y - P[b].y)<<std::endl;
        return 1ll * (P[a].x - P[b].x) * (P[a].y - P[b].y);
    }
    
    #define mid ((l + r) >> 1)
    #define ls(x) (x << 1)
    #define rs(x) (x << 1 | 1)
    
    inline void cover(int u, int l, int r, int w, int d) {  //要覆盖的点是w,层数是d
    //	std::cout<<u<<" "<<l<<" "<<r<<" "<<w<<" "<<d<<std::endl;
        if (P[v[d - 1][l]].x > P[w].x || P[v[d - 1][r]].y > P[w].y)
            return;
        if (in(v[d - 1][l],w) && in(v[d - 1][r], w)) {
    //        std::cout<<u<<" "<<l<<" "<<r<<" "<<mid<<" "<<w<<" "<<d<<std::endl;
            return void(need[u].push_back(w));
        }
        if (l == r)
            return;
        cover(ls(u), l, mid, w, d);
        cover(rs(u), mid + 1, r, w, d);
    }
    
    #define inf 1e18
    
    ll f[N];
    
    inline void solve(int u, int li, int ri, int l, int r, int d) {  //上一层是[li,ri],对[l,r]做分治的贡献
    //    	std::cout<<"扶桑大红花丶"<<u<<" "<<li<<" "<<ri<<" "<<l<<" "<<r<<" "<<d<<std::endl;
        if (l > r)
            return;
        int w = 0;  //中点转移的位置;
        ll val = inf;
    //    	std::cout<<"INTO FIND W"<<std::endl;
    //    	std::cout<<v[1][0]<<" "<<v[d - 1][li]<<" "<<P[v[d - 1][li]].x<<" "<<P[v[d - 1][li]].y<<" "<<P[need[u][mid]].x<<" "<<P[need[u][mid]].y<<std::endl;
        for (int i = li; i <= ri; ++i) {
            if (f[v[d - 1][i]] + 1ll * S(need[u][mid], v[d - 1][i]) < val)
                val = f[v[d - 1][i]] + 1ll * S(need[u][mid], v[d - 1][i]), w = i;
        }
        f[need[u][mid]] = std::min(f[need[u][mid]], val);
    //    	std::cout<<"FIND "<<w<<" "<<val<<std::endl;
        solve(u, w, ri, l, mid - 1, d);
        solve(u, li, w, mid + 1, r, d);
    }
    
    inline void dfs(int u, int l, int r, int d) {
    //    	std::cout<<"分治处理"<<std::endl;
    //    	std::cout<<u<<" "<<l<<" "<<r<<" "<<d<<std::endl;
        solve(u, l, r, 0, (int)need[u].size() - 1, d);
        need[u].clear();
        if (l == r)
        return;
        dfs(ls(u), l, mid, d);
        dfs(rs(u), mid + 1, r, d);
    }
    
    signed main() {
        freopen("grid.in", "r", stdin);
        freopen("grid.out", "w", stdout);
        scanf("%d%d", &n, &t);
        for (int i = 1; i <= n; ++i) {
            scanf("%d%d", &P[i].x, &P[i].y);
        }
        P[++n].x = 0, P[n].y = 0;
        P[++n].x = t, P[n].y = t;
        std::sort(P + 1, P + n + 1);
        for (int i = 1; i <= n; ++i) {
            P[i].ans = find(P[i].y - 1) + 1;
    //    	std::cout<<P[i].x<<" "<<P[i].y<<" "<<P[i].ans<<std::endl;
            add(P[i].y, P[i].ans);
        }  //
        int t = 0;  //层数
        for (int i = 1; i <= n; ++i) {
            v[P[i].ans].push_back(i);
            t = std::max(P[i].ans, t);
            f[i] = (P[i].ans == 1 ? 0 : inf);  //即答案
        }
        for (int i = 2; i <= t; ++i) {  //处理每一层
    //    	puts("LAS");
    //    	for(int j = 0;j < v[i - 1].size();++j)
    //    	std::cout<<v[i - 1][j]<<" "<<P[v[i - 1][j]].x<<" "<<P[v[i - 1][j]].y<<" "<<P[v[i -1][j]].ans<<std::endl;
    //		puts("DEL");
            for (int j = 0; j < (int)v[i].size(); ++j) {
    //            std::cout<<"FUCK "<<P[v[i][j]].x<<" "<<P[v[i][j]].y<<" "<<P[v[i][j]].ans<<std::endl;
                cover(1, 0, (int)v[i - 1].size() - 1, v[i][j], i);
            }
            dfs(1, 0, v[i - 1].size() - 1, i);
    //        for(int j = 0;j < (int)v[i].size();++j){
    ////        	std::cout<<"DO "<<P[v[i][j]].x<<" "<<P[v[i][j]].y<<" "<<P[v[i][j]].ans<<" "<<f[v[i][j]]<<std::endl;
    //        }
        }
        ll ans = inf;
        for (int i = 1; i <= n; ++i)
            if (P[i].ans == t)
                ans = std::min(ans, f[i]);
        std::cout << ans << std::endl;
    }
    /*
    5 20
    19 1
    2 6
    9 15
    10 3
    13 11
    */
    
    

    #574. 「二分图匹配」孤立点集

    考虑\(Dilworth\)定理,有偏序关系时,其的最长反链 = 最小不可重链覆盖。

    考虑\(DAG\)上的偏序关系实际上是其祖先链的关系,因为我们只关心最后的偏序关系链上的可重性,所以原图上其实是最小可重链覆盖。

    考虑最小可重链覆盖是经典题目,只要使用二分图匹配即可。

    考虑每个点的方案数就强制删去其可到达和他这个点再跑一次即可。

    「二分图匹配」孤立点集
    // code by fhq_treap
    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    #define ri register int
    const int maxn=110;
    bool del[maxn],e[maxn][maxn];
    int mchx[maxn],mchy[maxn],n,vis[maxn];
    bool dfs(int p,int t){
    	if(del[p]||vis[p]==t)return false;
    	vis[p]=t;
    	for(ri i=1;i<=n;++i)
    		if(!del[i]&&e[p][i]&&(!mchy[i]||dfs(mchy[i],t))){
    			mchx[p]=i;
    			mchy[i]=p;
    			return true;
    		}
    	return false;
    }
    int cnt;
    inline int calc(){
    	memset(mchx,0,sizeof mchx);
    	memset(mchy,0,sizeof mchy);
    	ri ret=0;
    	for(ri i=1;i<=n;++i)ret+=dfs(i,++cnt);
    	return ret;
    }
    bool tagx[maxn],tagy[maxn];
    void dfs(int k){
    	tagy[k]=true;
    	for(ri i=1;i<=n;++i)
    		if(e[i][k]&&!tagx[i]){
    			tagx[i]=true;
    			dfs(mchx[i]);
    		}
    }
    int ans,m;
    int main(){
    	freopen("isolated.in","r",stdin);
    	freopen("isolated.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	while(m--){
    		ri x,y;
    		scanf("%d%d",&x,&y);
    		e[x][y]=true;
    	}
    	for(ri k=1;k<=n;++k)
    		for(ri i=1;i<=n;++i)
    			if(e[i][k])
    				for(ri j=1;j<=n;++j)
    					if(e[k][j])
    						e[i][j]=true;
    	ans=n-calc();
    	printf("%d\n",ans);
    	for(ri i=1;i<=n;++i)
    		if(!mchy[i])
    			dfs(i);
    	for(ri i=1;i<=n;++i)putchar(tagx[i]^tagy[i]|48);
    	putchar(10);
    	for(ri i=1;i<=n;++i){
    		ri sum=n;
    		for(ri j=1;j<=n;++j)del[j]=(i==j||e[i][j]||e[j][i]),sum-=del[j];
    		putchar((sum-calc()==ans-1)|48);
    	}
    	return 0;
    }
    
    

    #976. 「母函数」随机减法

    考虑一层的答案相当于\(E[now] - E[las]\),发现其贡献具有可减性后,那我们直接跳过中间层计算整体的答案。

    考虑如何计算\(k\)轮过后的所有数期望乘积。

    不妨先写出柿子。

    \(E = \frac{1}{n^k}\sum_{\sum b_i = k}\frac{k!}{\prod b_i !}\prod (a_i - b_i)\)

    哇,我们一看,几把柿子不做了。

    考虑拆开贡献。

    \(E = \frac{k!}{n^k}\sum_{\sum {b_i = k}}\prod\frac{a_i - b_i}{b_i!}\)

    然后一看,哇可以卷积。

    \(k = 1e9\).

    考虑利用卷积的形式啊,\(sum\)的限制可以使用卷积解决其,前面的系数是平凡的。

    考虑如何处理后面这个。
    考虑使用生成函数。
    \(f_i(x) = (a_i - x)e^x\)

    那么就是\(F(x) = \prod_{i = 1}^n f_i e^{nx}= \sum_{i = 0}^n (a_i - x)\)

    \(G(x) = \prod_{i = 1} ^ n (a_i - x) = \sum_{i = 0}^n c_i x ^ i\)

    \([x^k]F(x) = \sum c_i \frac{n^{k - i}}{(k - i)!}\)

    直接\(O(n^2)\)就可以解决了。

    #976. 「母函数」随机减法
    #include<cstdio>
    #define ll long long
    #define mod 1000000007
    int n,k,e;
    ll t,ans=0,res,inv,mul=1;
    ll a[5002],c[5002]={};
    inline int min(int x,int y)
    {
    	return x<y? x:y;
    }
    
    inline ll qpow(ll a,ll b){
    	ll ans = 1;
    	while(b){
    		if(b & 1) ans = ans * a % mod;
    		a = a * a % mod;
    		b >>= 1;
    	}
    	return ans;
    }
    int main()
    {
    	freopen("calculate.in","r",stdin);
    	freopen("calculate.out","w",stdout);
    	scanf("%d%d",&n,&k),e=min(n,k),t=inv=qpow(n,mod-2),c[n]=1;
    	for(int i=1;i<=n;++i)
    	{
    		scanf("%lld",&a[i]);
    		for(int j=n-i;j<n;++j)c[j]=(c[j+1]*a[i]-c[j])%mod;
    		c[n]=-c[n];
    	}
    	for(int i=1;i<=e;++i,t=(t*inv)%mod)
    	{
    		res=t;
    		for(int j=k-i+1;j<=k;++j)res=(res*j)%mod;
    		ans=(ans-c[i]*res)%mod;
    	}
    	printf("%lld",(ans+mod)%mod);
    	return 0;
    }
    
    
    
    

    #594. 「费用流」大图书馆

    考虑如何同时取到\(k\)和图书的限制。

    我们发现其真是非常的困难。

    我们不如真难则反,思考一下如何操作,我们强制每个点都在买了一次就扔掉,然后考虑是否保留这本书。

    那么就可以用费用流刻画了。

    考虑\(k\)减一,每个点的剩余流量表示其为还能为其他书保留多少空间。

    那么转成了经典的区间覆盖最大权值点度有限的问题,构图不再赘述。

    #594. 「费用流」大图书馆
    #include<bits/stdc++.h>
    using namespace std;
    const int N=2009,inf=0x3f3f3f3f;
    #define ll long long
    typedef pair<int,ll>pii;
    
    struct Edge{int to,nxt,c,w;}e[N*2]; int hd[N],tot=1;
    void add(int u,int v,int c,int w){e[++tot]=(Edge){v,hd[u],c,w};hd[u]=tot;}
    void addh(int u,int v,int c,int w){
    //std::cout<<u<<" "<<v<<" "<<c<<" "<<w<<std::endl;
    	add(u,v,c,w),add(v,u,0,-w);
    }
    
    int n,k,s,t,mflow,tmp;
    ll cost;
    ll d[N]; bool in[N];
    bool spfa(){
    	queue<int>q; q.push(s); memset(d,0,sizeof(d)); d[s]=1;
    	while(!q.empty()) {
    		int u=q.front(); q.pop(); in[u]=0;
    		for(int i=hd[u],v;i;i=e[i].nxt)
    			if(e[i].c&&d[v=e[i].to]<d[u]+e[i].w) {
    				d[v]=d[u]+e[i].w;
    				if(!in[v]) q.push(v),in[v]=1;
    			}
    	}
    	return d[t]>0;
    }
    int dinic(int u,int flow) {
    	int rest=flow; if(u==t) return flow; in[u]=1;
    	for(int i=hd[u],v;i&&rest;i=e[i].nxt)
    		if(!in[v=e[i].to]&&e[i].c&&d[v]==d[u]+e[i].w) {
    			int used=dinic(v,min(e[i].c,rest));
    			if(!used) d[v]=-1;
    			rest-=used, e[i].c-=used, e[i^1].c+=used, cost+=used*e[i].w;
    		}
    	in[u]=0;
    	return flow-rest;
    }
    pii flow(int ret=0,int tmp=0) {
    	while(spfa()) while(tmp=dinic(s,inf)) ret+=tmp;
    	return make_pair(ret,cost);
    }
    //上面为最大费用最大流MCMF模板
    
    int las[N];
    int a[N],c[N];
    
    ll ans = 0;
    ll del = 0;
    ll sum = 0;
    
    int main() {
    	freopen("bibliotheca.in","r",stdin);
    	freopen("bibliotheca.out","w",stdout);
    	scanf("%d%d",&n,&k);
    	for(int i = 1;i <= n;++i)
    	scanf("%d",&a[i]);
    	for(int i = 1;i <= n;++i)
    	scanf("%d",&c[i]);
    	for(int i = 1;i <= n;++i){
    		if(las[a[i]] && las[a[i]] != i - 1)
    		addh(las[a[i]] + 1,i,1,c[a[i]]);
    		if(las[a[i]] && las[a[i]] == i - 1)
    		del += c[a[i]];
    		sum += c[a[i]];
    		las[a[i]] = i;
    	}
    	k -= 1;
    	s=n+1, t=n+2;
    	for(int i=1;i<n;i++) addh(i,i+1,k,0);
    	addh(s,1,k,0), addh(n,t,k,0);
    //	std::cout<<flow().second<<std::endl;
    //	std::cout<<del<<std::endl;
    	std::cout<<sum - del - flow().second<<std::endl;
    }
    

    #794. 「CDQ 分治 & 整体二分」奇度边集

    考虑可以构造出来的条件为所有的连通块点数为偶数。

    然后就是最小瓶颈树的过程。

    然后可以线段树分治了。

    「CDQ 分治 & 整体二分」奇度边集
    #include <bits/stdc++.h>
    #define MAXN 300005
    int n,m;
    int ans[MAXN];
    
    struct Edges{
        int u,v,w,id;
    } p[MAXN],q[MAXN];
    
    #define pii std::pair<int,int>
    
    struct DSU{
        int num,top;
        int fa[MAXN],size[MAXN];
        pii stk[MAXN];
    
        void init(int n) {num = n; for(int i = 1;i <= n;i++) fa[i] = i, size[i] = 1;}
        int find(int x) {return x == fa[x] ? x : find(fa[x]);}
    
        void merge(int x,int y){
            x = find(x); y = find(y);
            if(x == y) return;
            if(size[x] < size[y]) std::swap(x,y);
            num -= (size[x] & 1) + (size[y] & 1);
            fa[y] = x; size[x] += size[y]; num += (size[x] & 1);
            stk[++top] = std::make_pair(x,y);
        }
    
        void undo(){
            int x = stk[top].first, y = stk[top].second; top -= 1;
            num -= (size[x] & 1); size[x] -= size[y];
            fa[y] = y; num += (size[x] & 1) + (size[y] & 1);
        }
    } dsu;
    
    void solve(int l,int r,int x,int y){
        if(l > r) return;
        int mid = (l + r) >> 1, lst = dsu.top, ansmid = -1;
        for(int i = l;i <= mid;i++)
            if(q[i].id < x) dsu.merge(q[i].u,q[i].v);
        for(int i = x;i <= y;i++){
            if(p[i].id <= mid) dsu.merge(p[i].u,p[i].v);
            if(dsu.num == 0) {ansmid = i; break;}
        }
        while(dsu.top > lst) dsu.undo();
        if(ansmid == -1){
            for(int i = l;i <= mid;i++) ans[i] = -1;
            for(int i = l;i <= mid;i++)
                if(q[i].id < x) dsu.merge(q[i].u,q[i].v);
            solve(mid + 1,r,x,y);
            while(dsu.top > lst) dsu.undo(); return;
        }
        ans[mid] = p[ansmid].w;
        for(int i = l;i <= mid;i++)
            if(q[i].id < x) dsu.merge(q[i].u,q[i].v);
        solve(mid + 1,r,x,ansmid); while(dsu.top > lst) dsu.undo();
        for(int i = x;i <= ansmid;i++)
            if(p[i].id < l) dsu.merge(p[i].u,p[i].v);
        solve(l,mid - 1,ansmid,y); while(dsu.top > lst) dsu.undo();
    }
    
    bool cmp(const Edges &x,const Edges &y) {return x.w < y.w;}
    
    int main(){
        freopen("edges.in", "r", stdin);
        freopen("edges.out", "w", stdout);
        scanf("%d%d",&n,&m); dsu.init(n);
        for(int i = 1;i <= m;i++){
            scanf("%d%d%d",&q[i].u,&q[i].v,&q[i].w);
            p[i] = q[i]; p[i].id = i;
        }
        std::sort(p + 1,p + 1 + m,cmp);
        for(int i = 1;i <= m;i++) q[p[i].id].id = i;
        solve(1,m,1,m);
        for(int i = 1;i <= m;i++) printf("%d\n",ans[i]);
        return 0;
    }
    
    

    #915. 「欧拉函数」欧拉欧拉

    考虑\(max - min\)容斥可以转成\(lcm - gcd\)容斥

    那么发现其套上一个\(phi\)也是一样的。

    那么可以写出柿子:

    \(lcm(S) = \phi_{T \in S} gcd(T) ^ {(-1) ^ {|T| - 1}}\)

    于是答案变成\(\prod_{w = 1}^k \prod _{i1 = 1} ^ n \prod_{i2 = 1}^n...\prod_{iw = 1}(\phi(gcd(i1,i2,...,iw)))^{(-1)^{w - 1}\binom{k}{w}n^{k - w}}\)

    可以使用莫比乌斯反演即可。

    #634. 「左偏树」转移石子

    考虑其可以使用费用流模型操作,因为费用流的复杂度不对,于是我们自然的想到了使用了模拟费用流。

    考虑在树上操作,不如枚举路径的两端点的LCA,考虑把接受点和输出点分别开两个堆,则跑一次流量的的答案为\(d_x + d_y - 2*d_z - inf\),我们枚举\(z\)是固定的,把输出端\(d_x -inf\)在一个堆里,输入段在\(d_y\)在一个堆里,如果要退游时则考虑把\(2*d_z - B_y\)丢入输入堆表示可以换输出端,\(2d_z -A_x\)丢入输出端表示可以换输入堆。

    于是可以在树上写启发式合并优先队列\(O(nlog^2n)\)

    然后就光荣被卡常数退役了。

    于是使用平板电视里自带的可合并堆就行了。

    #634. 「左偏树」转移石子
    #include <bits/stdc++.h>
    #include <ext/pb_ds/priority_queue.hpp>
    #define file(x) freopen(#x".in","r",stdin); freopen(#x".out","w",stdout)
    #define mp make_pair
    using namespace std;
    typedef long long ll;
    
    int read() {
        int X = 0, w = 1;
        char c = getchar();
    
        while (c < '0' || c > '9') {
            if (c == '-')
                w = -1;
    
            c = getchar();
        }
    
        while (c >= '0' && c <= '9')
            X = X * 10 + c - '0', c = getchar();
    
        return X * w;
    }
    
    const int N = 250000 + 10;
    const ll inf = 1e12;
    
    int n, x[N], y[N];
    ll ans = 0;
    vector<pair<int, int>> E[N];
    struct node {
        ll cost;
        mutable int cnt;
    };
    bool operator <(node x, node y) {
        return x.cost < y.cost;
    }
    bool operator >(node x, node y) {
        return x.cost > y.cost;
    }
    __gnu_pbds::priority_queue<node, greater<node>> M[N], H[N];
    
    void dfs(int u, int fa, ll dep) {
        H[u].push((node) {
            dep, x[u]
        }), M[u].push((node) {
            dep - inf, y[u]
        });
    
        for (auto t : E[u]) {
            int v = t.first, w = t.second;
    
            if (v == fa)
                continue;
    
            dfs(v, u, dep + w);
            H[u].join(H[v]), M[u].join(M[v]);
        }
    
        while (!M[u].empty() && !H[u].empty()) {
            auto m = M[u].top(), h = H[u].top();
            ll cost = m.cost + h.cost - 2 * dep;
            int f = min(m.cnt, h.cnt);
    
            if (cost >= 0)
                break;
    
            ans += cost * f;
            M[u].top().cnt -= f;
    
            if (!M[u].top().cnt)
                M[u].pop();
    
            H[u].top().cnt -= f;
    
            if (!H[u].top().cnt)
                H[u].pop();
    
            M[u].push((node) {
                -cost + m.cost, f
                });
            H[u].push((node) {
                -cost + h.cost, f
                });
        }
    }
    
    int main() {
    	file(rock);
        n = read();
        int s = 0;
    
        for (int i = 1; i < n; ++i) {
            int u = read(), v = read(), w = read();
            E[u].emplace_back(mp(v, w)), E[v].emplace_back(mp(u, w));
        }
    
        for (int i = 1; i <= n; ++i)
            x[i] = read(), y[i] = read(), s += y[i];
    
        dfs(1, 0, 0);
        printf("%lld\n", ans + 1ll * s * inf);
        return 0;
    }
    

    #904. 「拉格朗日插值」网格序列

    考虑判断结论行列的数字只要最大的数字是一样就可以构造出答案。

    那么答案实际上为\(\sum_{i = 1}^k i^{n + m} - i^n(i - 1)^m - (i - 1)^ni^m + (i-1)^{n + m}\)

    其为一个\(n + m + 1\)项的多项式,所以可以使用拉格朗日插值考虑。

    考虑拉格朗日的公式\(f(k) = \sum_{i = 1}^c y_i \prod_{i != j} \frac{k - x_i}{x_i - x_j}\)

    不妨我们只处理出\(0~n + m\)的点值,此时\(x_i = i\)

    那么知道\((x) = \sum y_i \prod_{i != j}\frac{x - i}{i - j}\)

    那么知后面这个分数是很平凡的,那我们就可以\(O(n + m)\)插值出这多项式。

    #904. 「拉格朗日插值」网格序列
    #include <bits/stdc++.h>
    
    #define MOD 998244353
    #define ll long long
    
    using namespace std;
    
    ll pow_mod(ll x, int k) {
        ll result = 1;
        while (k) {
            if (k & 1)
                result = result * x % MOD;
            x = x * x % MOD;
            k >>= 1;
        }
        return result;
    }
    
    ll facd[2000005], facv[2000005];
    
    void pre(int n) {
        facd[0] = 1;
        for (int i = 1; i <= n; i++) facd[i] = facd[i - 1] * i % MOD;
        facv[n] = pow_mod(facd[n], MOD - 2);
        for (int i = n - 1; i >= 0; i--) facv[i] = facv[i + 1] * (i + 1) % MOD;
    }
    
    ll f[2000005];
    
    ll lagrange(int n, int k) {
        static ll l[2000005], r[2000005];
        if (k <= n + 1)
            return f[k];
        l[0] = r[n + 2] = 1;
        for (int i = 1; i <= n + 1; i++) l[i] = l[i - 1] * (k - i) % MOD;
        for (int i = n + 1; i > 0; i--) r[i] = r[i + 1] * (k - i) % MOD;
        ll result = 0;
        for (int i = 1; i <= n + 1; i++)
            result = (result + facv[i - 1] * facv[n - i + 1] % MOD * (((n - i) & 1) ? 1 : MOD - 1) % MOD *
                                   l[i - 1] % MOD * r[i + 1] % MOD * f[i]) %
                     MOD;
        return result;
    }
    
    int main() {
        freopen("grid.in", "r", stdin);
        freopen("grid.out", "w", stdout);
        int T = 1;
        while (T--) {
            int n, m, k;
            scanf("%d%d%d", &n, &m, &k);
            pre(n + m);
            for (int i = 1; i <= n + m; i++)
                f[i] = (f[i - 1] +
                        (pow_mod(i, n) - pow_mod(i - 1, n) + MOD) * (pow_mod(i, m) - pow_mod(i - 1, m) + MOD)) %
                       MOD;
            printf("%lld\n", lagrange(n + m - 1, k));
        }
        return 0;
    }
    
    

    #764. 「启发式合并」交换游戏

    考虑在A上做启发式合并,在\(B\)上做启发式合并,发现如果考虑把\((u,v)\)的话,\(B\)选的一定是在\(u \to lca,v\to lca\)的路径上的,且两点在\(A\)中应该是分属子树两边。

    #764. 「启发式合并」交换游戏
    #include<bits/stdc++.h>
    #define pb push_back
    using namespace std;
    
    const int N=2e5+10,M=N*60;
    
    namespace IO
    {
    	int read()
    	{
    		int ret=0;char c=getchar();
    		while(!isdigit(c)) c=getchar();
    		while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
    		return ret;
    	}
    	void write(int x){if(x>9)write(x/10);putchar(x%10^48);}
    	void writesp(int x){write(x);putchar(' ');}
    }
    using namespace IO;
    
    namespace Segment
    {
    	int rt[N];
    	struct tr
    	{
    		int sz,sum[M],ls[M],rs[M];
    		void update(int &x,int l,int r,int p,int v)
    		{
    			if(!x) x=++sz;sum[x]+=v;
    			if(l==r) return;
    			int mid=(l+r)>>1;
    			if(p<=mid) update(ls[x],l,mid,p,v);
    			else update(rs[x],mid+1,r,p,v);
    		}
    		int query(int x,int l,int r,int L,int R)
    		{
    			//printf("Q:%d %d %d %d %d\n",x,l,r,L,R);
    			if(L>R || !x) return 0;
    			if(L<=l && r<=R) return sum[x];
    			int mid=(l+r)>>1,res=0;
    			if(L<=mid) res+=query(ls[x],l,mid,L,R);
    			if(R>mid) res+=query(rs[x],mid+1,r,L,R);
    			return res;
    		}
    		int merge(int x,int y,int l,int r)
    		{
    			if(!x || !y) return x+y;
    			int mid=(l+r)>>1,z=++sz;
    			sum[z]=sum[x]+sum[y];
    			if(l^r)	ls[z]=merge(ls[x],ls[y],l,mid),rs[z]=merge(rs[x],rs[y],mid+1,r);
    			return z;
    		}
    		void clear()
    		{
    			for(int i=0;i<=sz;++i) sum[i]=ls[i]=rs[i]=0;
    			sz=0;
    		}
    		void print(int x,int l,int r)
    		{
    			printf("%d %d %d %d\n",x,l,r,sum[x]);
    			if(l==r) return;
    			int mid=(l+r)>>1;
    			print(ls[x],l,mid);print(rs[x],mid+1,r);
    		}
    	}tr;
    }
    using namespace Segment;
    
    namespace Tree
    {
    	struct Tway{int v,nex,id;};
    	struct Tree
    	{
    		int tot,ind;
    		int head[N],top[N],son[N],fa[N],siz[N],pos[N],dep[N];
    		Tway e[N<<1];
    		void add(int u,int v,int id)
    		{
    			e[++tot]=(Tway){v,head[u],id};head[u]=tot;
    			e[++tot]=(Tway){u,head[v],id};head[v]=tot;
    		}
    		void dfs1(int x)
    		{
    			siz[x]=1;
    			for(int i=head[x];i;i=e[i].nex)
    			{
    				int v=e[i].v;
    				if(v==fa[x]) continue;
    				fa[v]=x;dep[v]=dep[x]+1;dfs1(v);siz[x]+=siz[v];
    				if(siz[v]>siz[son[x]]) son[x]=v;
    			}
    		}
    		void dfs2(int x,int tp)
    		{
    			top[x]=tp;pos[x]=++ind;
    			if(son[x]) dfs2(son[x],tp);
    			for(int i=head[x];i;i=e[i].nex)
    			{
    				int v=e[i].v;
    				if(v==fa[x] || v==son[x]) continue;
    				dfs2(v,v);
    			}
    		}
    		void build(){dfs1(1);dfs2(1,1);}
    		int lca(int x,int y)
    		{
    			while(top[x]^top[y])
    			{
    				if(dep[top[x]]>=dep[top[y]])x=fa[top[x]];
    				else y=fa[top[y]];
    			}
    			return dep[x]<dep[y]?x:y;
    		}
    		void clear()
    		{
    			for(int i=0;i<=ind;++i) head[i]=top[i]=dep[i]=fa[i]=siz[i]=son[i]=pos[i]=0;
    			tot=ind=0;
    		}
    	}T1,T2;
    }
    using namespace Tree;
    
    namespace DreamLolita
    {
    	int n,ans[N],fr[N];
    	vector<int>tag[N];
    	int querychain(int root,int x,int y)
    	{
    		//printf("query:%d %d\n",x,y);
    		int res=0;
    		while(T2.top[x]^T2.top[y])
    		{
    			if(T2.dep[T2.top[x]]<T2.dep[T2.top[y]]) swap(x,y);//should jump x
    			res+=tr.query(root,1,n,T2.pos[T2.top[x]],T2.pos[x]);x=T2.fa[T2.top[x]];
    		}
    		if(T2.dep[x]<T2.dep[y]) swap(x,y);
    		//printf("%d %d %d\n",root,T2.pos[y]+1,T2.pos[x]);
    		res+=tr.query(root,1,n,T2.pos[y]+1,T2.pos[x]);//-1 because no lca
    		return res;
    	}
    	void dfstag(int x)//put tag on T1,so dfs T2
    	{
    		for(int i=T2.head[x];i;i=T2.e[i].nex)
    		{
    			int v=T2.e[i].v,d=T2.e[i].id;
    			if(v==T2.fa[x]) continue;
    			tag[v].pb(d);tag[x].pb(d);tag[T1.lca(x,v)].pb(-d);
    			fr[d]=v;dfstag(v);//point v maintain edge d on T2
    		}
    	}
    	void dfs(int x,int d)//calc ans,so dfs T1,and add  on T2,query on T2,use Heavy_Light cut
    	{
    		for(int i=T1.head[x];i;i=T1.e[i].nex)//first dfs then calc
    		{
    			int v=T1.e[i].v;
    			if(v==T1.fa[x]) continue;
    			dfs(v,T1.e[i].id);rt[x]=tr.merge(rt[x],rt[v],1,n);
    		}
    		if(x==1) return;
    		for(auto i:tag[x])//push tag,+1 or -2
    		{
    			if(i>0) tr.update(rt[x],1,n,T2.pos[fr[i]],1);
    			else tr.update(rt[x],1,n,T2.pos[fr[-i]],-2);
    		}
    		//printf("now:%d\n",x);tr.print(rt[x],1,n);puts("");
    		ans[d]=querychain(rt[x],x,T1.fa[x]);
    	}
    	void clear()
    	{
    		T1.clear();T2.clear();tr.clear();
    		for(int i=0;i<=n;++i) tag[i].clear(),fr[i]=0,rt[i]=0;
    	}
    	void solution()
    	{
    		n=read();
    		for(int i=1;i<n;++i) T1.add(read(),read(),i);
    		for(int i=1;i<n;++i) T2.add(read(),read(),i);
    		T1.build();T2.build();dfstag(1);dfs(1,0);
    		for(int i=1;i<n;++i) writesp(ans[i]); puts("");
    		clear();
    	}
    }
    
    
    int main()
    {
    	freopen("exchange.in","r",stdin);
    	freopen("exchange.out","w",stdout);
    	DreamLolita::solution();
    	return 0;
    }
    
    

    #704. 「树链剖分」树的核心

    考虑求的是一个带修的带权重心问题,我们发现其\(x\)为根的1所在的子树等价于\(1\)为根的时,\(x\)为根的子树补,其大小小于\(\frac{1}{2}\),所以其\(x\)的子树一定大于\(\frac{1}{2}\),那么按\(dfn\)序,权值和的中点一定在\(x\)的子树里,二分找到他,然后考虑倍增找到深度最大的满足子树大小大于\(\frac{1}{2}\)的点。

    #704. 「树链剖分」树的核心
    #include <bits/stdc++.h>
    using namespace std;
    #define re register
    #define ll long long
    inline int read() {
        int f = 1, lzx = 0;
        char c = getchar();
        while (c > '9' || c < '0') {
            if (c == '-')
                f = -f;
            c = getchar();
        }
        while (c <= '9' && c >= '0') {
            lzx = lzx * 10 + c - '0';
            c = getchar();
        }
        return lzx * f;
    }
    const int N = 1e5 + 10;
    int n, q, fa[N][20], head[N], cnt, to[N], from[N], sz[N], dep[N], top[N], dfn[N], rev[N], T, heavy[N];
    ll sum[N * 4], tag[N * 4], all;
    inline void link(int x, int y) {
        from[++cnt] = head[x];
        head[x] = cnt;
        to[cnt] = y;
        return;
    }
    inline void dfs1(int x) {
        dep[x] = dep[fa[x][0]] + 1;
        sz[x] = 1;
        for (re int i = head[x]; i; i = from[i]) {
            int v = to[i];
            dfs1(v);
            sz[x] += sz[v];
            if (sz[v] > sz[heavy[x]])
                heavy[x] = v;
        }
        return;
    }
    inline void dfs2(int x) {
        dfn[x] = ++T;
        rev[T] = x;
        if (!top[x])
            top[x] = x;
        if (heavy[x]) {
            top[heavy[x]] = top[x];
            dfs2(heavy[x]);
        }
        for (re int i = head[x]; i; i = from[i]) {
            int v = to[i];
            if (v == heavy[x])
                continue;
            dfs2(v);
        }
        return;
    }
    inline void pushdown(int l, int r, int k) {
        if (tag[k]) {
            int mid = l + r >> 1;
            sum[k << 1] += tag[k] * (mid - l + 1);
            sum[k << 1 | 1] += tag[k] * (r - mid);
            tag[k << 1] += tag[k];
            tag[k << 1 | 1] += tag[k];
            tag[k] = 0;
        }
        return;
    }
    inline void change(int l, int r, int x, int y, int k) {
        if (l > y || r < x)
            return;
        if (l >= x && r <= y) {
            sum[k] += r - l + 1;
            tag[k]++;
            return;
        }
        int mid = l + r >> 1;
        pushdown(l, r, k);
        change(l, mid, x, y, k << 1);
        change(mid + 1, r, x, y, k << 1 | 1);
        sum[k] = sum[k << 1] + sum[k << 1 | 1];
        return;
    }
    inline void add(int x, int y) {
        while (top[x] != top[y]) {
            if (dep[top[x]] < dep[top[y]])
                swap(x, y);
            change(1, n, dfn[top[x]], dfn[x], 1);
            x = fa[top[x]][0];
        }
        if (dep[x] > dep[y])
            swap(x, y);
        change(1, n, dfn[x], dfn[y], 1);
        return;
    }
    inline int getpos(int l, int r, int k, ll res) {
        if (l == r)
            return l;
        int mid = l + r >> 1;
        pushdown(l, r, k);
        if ((sum[k << 1] + res) * 2 >= all)
            return getpos(l, mid, k << 1, res);
        return getpos(mid + 1, r, k << 1 | 1, res + sum[k << 1]);
    }
    inline ll query(int l, int r, int x, int y, int k) {
        if (l >= x && r <= y)
            return sum[k];
        if (l > y || r < x)
            return 0;
        int mid = l + r >> 1;
        pushdown(l, r, k);
        return query(l, mid, x, y, k << 1) + query(mid + 1, r, x, y, k << 1 | 1);
    }
    int main() {
        freopen("core.in", "r", stdin);
        freopen("core.out", "w", stdout);
        n = read(), q = read();
        for (re int i = 1; i < n; i++) {
            fa[i + 1][0] = read();
            link(fa[i + 1][0], i + 1);
        }
        for (re int i = 1; i < 20; i++)
            for (re int j = 1; j <= n; j++) fa[j][i] = fa[fa[j][i - 1]][i - 1];
        dfs1(1);
        dfs2(1);
        while (q--) {
            int op = read();
            if (op == 2) {
                int u = read(), v = read();
                add(u, v);
            } else {
                int u = read();
                change(1, n, dfn[u], dfn[u] + sz[u] - 1, 1);
            }
            all = sum[1];
            int pos = getpos(1, n, 1, 0);
            pos = rev[pos];
            for (re int i = 19; i >= 0; i--) {
                if (!fa[pos][i])
                    continue;
                if (query(1, n, dfn[fa[pos][i]], dfn[fa[pos][i]] + sz[fa[pos][i]] - 1, 1) * 2 <= all)
                    pos = fa[pos][i];
            }
            if (query(1, n, dfn[pos], dfn[pos] + sz[pos] - 1, 1) * 2 <= all)
                printf("%d\n", fa[pos][0]);
            else
                std::cout << pos << "\n";
        }
        return 0;
    }
    
    

    #944. 「莫比乌斯反演」随机添数

    考虑期望的典中典中典公式。

    \(E(x) = \sum_{1 \leq i}P(i \leq x)\)

    考虑答案实际上等价于\(i - 1\)\(gcd > 1\),那么考虑全局是很好求的,那么只要求\(gcd = 1\)的方案。

    \(\sum_{1\leq a_i \leq m}[gcd(a_i) = 1]\)

    考虑莫反一下则有\(\sum_{d = 1}^m\mu(d)\lfloor\frac{m}{d}\rfloor^{i - 1}\)

    image

    image

    image

    #944. 「莫比乌斯反演」随机添数
    // code by fhq_treap
    #include<bits/stdc++.h>
    #define ll long long
    #define N 300005
    #define uint unsigned int
    
    inline ll read(){
        char C=getchar();
        ll A=0 , F=1;
        while(('0' > C || C > '9') && (C != '-')) C=getchar();
        if(C == '-') F=-1 , C=getchar();
        while('0' <= C && C <= '9') A=(A << 1)+(A << 3)+(C - 48) , C=getchar();
        return A*F;
    }
    
    template <typename T>
    void write(T x)
    {
        if(x < 0) {
            putchar('-');
            x = -x;
        }
        if(x > 9)
            write(x/10);
        putchar(x % 10 + '0');
        return;
    }
    
    int n;
    int mu[N];
    int v[N];
    int s[N];
    
    #define mod 1000000007
    
    int p[N],t;
    
    inline void Sieve(){
    	mu[1] = 1;
    	for(int i = 2;i <= 1e5;++i){
    		if(!v[i]){
    			mu[i] = -1;
    			p[++t] = i;
    		}
    		for(int j = 1;j <= t && p[j] * i <= 1e5;++j){
    			v[p[j] * i] = 1;
    			if(i % p[j] == 0){
    				mu[i * p[j]] = 0;
    				break;
    			}else
    				mu[i * p[j]] = - mu[i];
    		}
    	}
    	for(int i = 1;i <= 1e5;++i)
    	s[i] = (s[i - 1] + mu[i] + mod) % mod;
    }
    
    inline ll qpow(ll a,ll b){
    	ll ans = 1;
    	while(b){
    		if(b & 1)ans = ans * a % mod;
    		a = a * a % mod;
    		b >>= 1;
    	}
    	return ans;
    }
    
    int T;
    uint ans = 0;
    uint inv[N];
    
    int main(){
    	freopen("random.in","r",stdin);
    	freopen("random.out","w",stdout);
    	scanf("%d",&T);
    	Sieve();
    	for(int i = 1;i < N;++i)
    	inv[i] = qpow(i,mod - 2);
    	while(T -- ){
    		n = read();
    		int l = 2,r;
    		ans = 1;
    		for(;l <= n;l = r + 1){
    			r = n / (n / l);
    			ans = (ans + (mod - 1) * (s[r] - s[l - 1] + mod) % mod * (n / l) % mod * inv[n - n / l] % mod);
    			if(ans > mod)
    			ans -= mod;
    		}
    		write(ans);
    		puts("");
    	}
    }
    
    
    

    #964. 「FFT」字符匹配

    考虑对字符集的每个字符进行操作:

    \(A_i\)表示为是否能够被当前字符范围覆盖,\(B_i\)表示第二个字符串是否是这位。

    考虑\(F_i = \sum A_{i + k} * B_i\)\(F_i\)等于第二个字符串里的该字符数量则该字符可以被从\(i\)被匹配。

    把所有字符答案交起来即可。

    #964. 「FFT」字符匹配
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    typedef long long ll;
    typedef double ld;
    inline int Max(int x, int y) { return x > y ? x : y; }
    inline int Min(int x, int y) { return x < y ? x : y; }
    const int N = 800010;
    const ld pi = acos(-1.0);
    struct cpx {
    	ld x, y;
    	cpx(ld xx = 0, ld yy = 0) { x = xx; y = yy; }
    };
    cpx operator + (cpx a, cpx b) { return cpx(a.x + b.x, a.y + b.y); }
    cpx operator - (cpx a, cpx b) { return cpx(a.x - b.x, a.y - b.y); }
    cpx operator * (cpx a, cpx b) { return cpx(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); }
    cpx *getw(int n, int type) {
    	static cpx w[N/2];
    	w[0] = cpx(1, 0); w[1] = cpx(cos(2 * pi / n), sin(2 * pi / n) * type);
    	for(int i = 2; i < n/2; ++i) w[i] = w[i-1] * w[1];
    	return w;
    }
    int p[N];
    void FFT(cpx *a, int n, int type) {
    	for(int i = 0; i < n; ++i) if(i < p[i]) std::swap(a[i], a[p[i]]);
    	for(int i = 1; i < n; i <<= 1) {
    		cpx *w = getw(i << 1, type);
    		for(int j = 0; j < n; j += i << 1) {
    			cpx *b = a + j, *c = b + i;
    			for(int k = 0; k < i; ++k) {
    				cpx v = w[k] * c[k];
    				c[k] = b[k] - v;
    				b[k] = b[k] + v;
    			}
    		}
    	}
    	if(type == -1) for(int i = 0; i < n; ++i) a[i].x /= n;
    }
    void mul(int *a, int *b, int *c, int n, int m) {
    	static cpx f[N], g[N];
    	int len = 1, ct = 0;
    	while(len <= n + m) len <<= 1, ++ct;
    	for(int i = 0; i < len; ++i) p[i] = (p[i>>1]>>1) | ((i&1) << (ct-1));
    	for(int i = 0; i < len; ++i) f[i] = g[i] = cpx(0, 0);
    	for(int i = 0; i < n; ++i) f[i] = cpx(a[i], 0);
    	for(int i = 0; i < m; ++i) g[i] = cpx(b[i], 0);
    	FFT(f, len, 1);
    	FFT(g, len, 1);
    	for(int i = 0; i < len; ++i) f[i] = f[i] * g[i];
    	FFT(f, len, -1);
    	for(int i = 0; i < len; ++i) c[i] = (int)(f[i].x+0.5);
    }
    int n, m, k;
    int id[300], c[10][N], A[10][N];
    int s[N], t[N];
    int f[N], g[N];
    signed main() {
    	freopen("string.in","r",stdin);
    	freopen("string.out","w",stdout);
    	scanf("%d%d%d", &n, &m, &k);
    	for(int i = 0;i <= 9;++i)
    	id[i] = i;
    	for(int i = 0;i < n;++i)
    	scanf("%1d",&s[i]);
    	for(int i = 0;i < m;++i)
    	scanf("%1d",&t[i]);
    	for(int i = 0; i < n; ++i) {
    		s[i] = id[(int)s[i]]; t[i] = id[(int)t[i]];
    		++c[(int)s[i]][Max(i-k, 0)];
    		--c[(int)s[i]][Min(i+k+1, n)];
    	}
    	for(int i = 1; i < n; ++i) for(int j = 0; j <= 9; ++j) c[j][i] += c[j][i-1];
    	for(int o = 0; o <= 9; ++o) {
    		for(int i = 0; i < n; ++i)
    			f[i] = c[o][i] ? 0 : 1;
    		for(int i = 0; i < m; ++i)
    			g[i] = t[i] == o ? 1 : 0;
    		std::reverse(g, g + m);
    		mul(f, g, A[o], n, m);
    	}
    	int ans = 0;
    	for(int i = m-1; i < n; ++i){
    		bool k = 1;
    		for(int j = 0;j <= 9;++j)
    		k = k & !A[j][i];
    		if(k)
    		++ans;
    	}
    	printf("%d\n", ans);
    	return 0;
    }
    
  • 相关阅读:
    Nokia Lumia 800销售反馈 苹果iPhone、三星Galaxy不敌800设计
    各大网站用户数据库被爆,遭大量网友下载
    最美发明家:GPS、手机通讯网都源自她的发明
    iPhone5或明年下半年发布 配备iOS6和A6芯片
    保存文件到手机内存
    2012年十大科技趋势:NFC、语音控制与弯曲屏
    电脑报独家报道:宽带升级全国真相调查
    Android的电话拨号器
    Java程序员成长之路(接口与抽象类究竟有什么区别)
    联系人相关
  • 原文地址:https://www.cnblogs.com/dixiao/p/15875407.html
Copyright © 2020-2023  润新知