zoukankan      html  css  js  c++  java
  • XVIII Open Cup named after E.V. Pankratiev. GP of Romania

    A. Balance

    不难发现确定第一行第一列后即可确定全部,列不等式单纯形求解线性规划即可。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    typedef vector<double>VD;
    const int N=110;
    const double eps=1e-9;
    VD simplex(vector<VD>A, VD b, VD c){
    	int n = A.size(), m = A[0].size() + 1, r = n, s = m - 1;
    	vector<VD> D(n + 2, VD(m + 1, 0)); vector<int> ix(n + m);
    	for(int i = 0; i < n + m; i ++) ix[i] = i;
    	for(int i = 0; i < n; i ++){
    		for(int j = 0; j < m - 1; j ++) D[i][j] = -A[i][j];
    		D[i][m - 1] = 1; D[i][m] = b[i];
    		if(D[r][m] > D[i][m]) r = i;
    	}
    	for(int j = 0; j < m - 1; j ++) D[n][j] = c[j];
    	D[n + 1][m - 1] = -1;
    	for(double d; ;){
    		if(r < n){
    			int t = ix[s]; ix[s] = ix[r + m]; ix[r + m] = t;
    			D[r][s] = 1.0 / D[r][s]; vector<int> speedUp;
    			for(int j = 0; j <= m; j ++) if(j != s){
    				D[r][j] *= -D[r][s];
    				if(D[r][j]) speedUp.push_back(j);
    			}
    			for(int i = 0; i <= n + 1; i ++) if(i != r){
    				for(int j = 0; j < speedUp.size(); j ++)
    					D[i][speedUp[j]] += D[r][speedUp[j]] * D[i][s];
    				D[i][s] *= D[r][s];
    			}
    		}
    		r = -1; s = -1;
    		for(int j = 0; j < m; j ++) if(s < 0 || ix[s] > ix[j])
    			if(D[n + 1][j] > eps || (D[n + 1][j] > -eps && D[n][j] > eps)) s = j;
    		if(s < 0) break;
    		for(int i = 0; i < n; i ++) if(D[i][s] < -eps)
    			if(r < 0 || (d = D[r][m] / D[r][s] - D[i][m] / D[i][s]) < -eps
    				|| (d < eps && ix[r + m] > ix[i + m])) r = i;
    		if(r < 0) return VD();
    	}
    	if(D[n + 1][m] < -eps) return VD();
    	VD x(m - 1);
    	for(int i = m; i < n + m; i ++) if(ix[i] < m - 1) x[ix[i]] = D[i - m][m];
    	printf("%.0f
    ",-D[n][m]);
    	return x;	
    }
    int n,cnt,i,j,k;int a[N][N],f[N][N][N],s[N];double w[N];
    int main(){
    	scanf("%d",&n);
    	for(i=1;i<=n;i++)for(j=1;j<=n;j++)scanf("%d",&a[i][j]);
    	cnt=n*2-1;
    	for(i=1;i<=n;i++){
    		f[1][i][i]=1;
    		if(i>1)f[i][1][i+n-1]=1;
    	}
    	for(i=2;i<=n;i++)for(j=2;j<=n;j++)for(k=1;k<=cnt;k++)f[i][j][k]=f[i-1][j][k]+f[i][j-1][k]-f[i-1][j-1][k];
    	for(i=1;i<=n;i++)for(j=1;j<=n;j++)for(k=1;k<=cnt;k++)s[k]+=f[i][j][k];
    	VD c;
    	vector<VD>A;
    	VD b;
    	for(k=1;k<=cnt;k++)c.push_back(-s[k]);
    	for(i=1;i<=n;i++)for(j=1;j<=n;j++){
    		b.push_back(-a[i][j]);
    		VD t;
    		for(k=1;k<=cnt;k++)t.push_back(-f[i][j][k]);
    		A.push_back(t);
    	}
    	VD ret=simplex(A,b,c);
    	for(i=1;i<=cnt;i++)w[i]=ret[i-1];
    	for(i=1;i<=n;puts(""),i++)for(j=1;j<=n;j++){
    		double t=0;
    		for(k=1;k<=cnt;k++)t+=w[k]*f[i][j][k];
    		printf("%.0f ",t);
    	}
    }
    /*
    4
    1 1 1 1
    1 1 1 1
    1 1 1 0
    1 1 1 1
    */
    

      

    B. Entanglement

    留坑。

    C. Gravity

    对于每个连通块设$f_x$表示$x$连通块往下掉了多少,对于同一列相邻两个关于$f$建图求最短路即可。

    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    #include<string>
    #include<ctype.h>
    #include<math.h>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    #include<bitset>
    #include<algorithm>
    #include<time.h>
    using namespace std;
    void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
    #define MS(x, y) memset(x, y, sizeof(x))
    #define ls o<<1
    #define rs o<<1|1
    typedef long long LL;
    typedef unsigned long long UL;
    typedef unsigned int UI;
    template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
    template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
    const int N = 2020, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
    template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
    int n, m;
    char s[N][N];
    const int dy[4] = {-1,0,0,1};
    const int dx[4] = {0,-1,1,0};
    int id[N][N];
    int ID;
    void go(int y, int x)
    {
    	id[y][x] = ID;
    	for(int k = 0; k < 4; ++k)
    	{
    		int yy = y + dy[k];
    		int xx = x + dx[k];
    		if(s[yy][xx] == '#' && !id[yy][xx])
    		{
    			go(yy, xx);
    		}
    	}
    }
    int boty[N], boto[N];
    int f[N * N];
    vector< pair<int,int> >a[N * N];
    struct A
    {
    	int x, dis;
    	bool operator < (const A & b)const
    	{
    		return dis > b.dis;
    	}
    };
    void ins(int x, int y, int z)
    {
    	a[x].push_back({y,z});
    }
    bool e[N * N];
    void dijk()
    {
    	MS(f, 63);
    	f[0] = 0;
    	priority_queue<A>q;
    	q.push({0, 0});
    	MS(e, 0);
    	while(!q.empty())
    	{
    		int x = q.top().x; q.pop(); 
    		if(e[x])continue; e[x] = 1;
    		for(auto it : a[x])
    		{
    			int y = it.first;
    			int z = it.second;
    			if(f[x] + z < f[y])
    			{
    				f[y] = f[x] + z;
    				q.push({y, f[y]});
    			}
    		}
    	}
    	//puts("dijk finish");
    }
    char ans[N][N];
    int main()
    {
    	while(~scanf("%d%d", &n, &m))
    	{
    		MS(s, 0);
    		MS(id, 0);
    		ID = 0;
    		for(int i = 1; i <= n; ++i)
    		{
    			scanf("%s", s[i] + 1);
    		}
    		for(int i = 1; i <= n; ++i)
    		{
    			for(int j = 1; j <= m; ++j)
    			{
    				if(s[i][j] == '#' && !id[i][j])
    				{
    					++ID;
    					go(i, j);
    				}
    			}
    		}
    		MS(boto, 0);
    		for(int j = 1; j <= m; ++j)boty[j] = n + 1;
    		for(int j = 1; j <= m; ++j)
    		{
    			for(int i = n; i >= 1; --i)if(s[i][j] == '#')
    			{
    				//printf("%d %d
    ", i, j);
    				int x = id[i][j];
    				int y = boto[j];
    				if(x != y)ins(y, x, boty[j] - i - 1);
    				boto[j] = x;
    				boty[j] = i;
    			}
    		}
    		//continue;
    		//puts("before dijk()");
    		dijk();
    		MS(ans, 0);
    		for(int i = 1; i <= n; ++i)
    		{
    			for(int j = 1; j <= m; ++j)
    			{
    				ans[i][j] = '.';
    			}
    		}
    		for(int i = 1; i <= n; ++i)
    		{
    			for(int j = 1; j <= m; ++j)if(s[i][j] == '#')
    			{
    				int x = id[i][j];
    				//printf("%d %d %d
    ", i, j, f[x]);
    				ans[i + f[x]][j] = '#';
    			}
    		}
    		for(int i = 1; i <= n; ++i)puts(ans[i] + 1);
    		for(int i = 1; i <= ID; ++i)a[i].clear();
    	}
    	return 0;
    }
    /*
    【trick&&吐槽】
    10 10
    ..........
    ..######..
    ..#....#..
    ..#.#..#..
    ..#..#.#..
    ..#....#..
    ..######..
    ..........
    ..#....#..
    .......#..
    
    3 3
    #.#
    .#.
    ..#
    
    6 6
    ######
    .....#
    .###.#
    .###.#
    .....#
    ######
    
    【题意】
    
    
    【分析】
    
    
    【时间复杂度&&优化】
    
    
    */
    

      

    D. Infinite Pattern Matching

    枚举位数,设$f[i][j][S]$表示还剩低$i$位未考虑,目前KMP指针为$j$,前面KMP转移表为$S$是否搜过,记忆化搜索即可。

    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<map>
    #include<cstdlib>
    #include<iostream>
    using namespace std;
    typedef long long ll;
    int g[2][60],ans;
    map<string,int>f[100][60];
    int m,i,j,k,nxt[60];char a[60],q[60];
    ll pre;
    int dfs(int n,int x,string s,int prelen,ll pre){
      if(!n){
        if(s[x]==m){
          for(int i=1;i<=prelen&&x<m;i++){
            x=g[q[i]][x];
            pre++;
          }
          cout<<pre;
          exit(0);
        }
        return s[x];
      }
      if(f[n][x].find(s)!=f[n][x].end())return f[n][x][s];
      int v=x;
      for(int i=0;i<2;i++){
        string o=s;
        q[prelen-n+1]=i;
        for(int j=0;j<=m;j++)o[j]=g[i][s[j]];
        v=dfs(n-1,v,o,prelen,pre);
        pre+=((ll)prelen)<<(n-1);
      }
      return f[n][x][s]=v;
    }
    int main(){
      scanf("%s",a+1);
      m=strlen(a+1);
      for(i=1;i<=m;i++)a[i]-='0';
      for(nxt[1]=j=0,i=2;i<=m;nxt[i++]=j){
        while(j&&a[j+1]!=a[i])j=nxt[j];
        if(a[j+1]==a[i])j++;
      }
      for(i=0;i<m;i++)for(j=0;j<2;j++){
        for(k=i;k&&a[k+1]!=j;k=nxt[k]);
        if(a[k+1]==j)k++;
        g[j][i]=k;
      }
      g[0][m]=g[1][m]=m;
      for(i=0;;i++){
        string o="";
        for(k=0;k<=m;k++)o.push_back(g[1][k]);
        q[1]=1;
        ans=dfs(i,ans,o,i+1,pre);
        pre+=((ll)(i+1))<<i;
      }
    }
    

      

    E. Inheritance

    留坑。

    F. Movies

    留坑。

    G. Origami

    不难发现行列独立,转化为一维字符串问题。

    对于一次折叠,就是边界通过某个偶回文半径进行了翻折,Manacher预处理出回文半径后DP求出每个位置是否可以由左右边界翻折得到即可。

    时间复杂度$O(nm)$。

    #include<cstdio>
    typedef long long ll;
    const int N=2000010;
    int n,m,i,j,r,p,f[N],g[N],pre[N],suf[N];ll a[N],b[N],s[N];char ch[N];
    inline int min(int a,int b){return a<b?a:b;}
    ll solve(ll*a,int n){
      int m;
      for(i=1;i<=n;i++)s[i<<1]=a[i],s[i<<1|1]=-1;
      s[0]=-2;s[1]=-1;s[m=(n+1)<<1]=-3;
      for(r=p=0,f[1]=1,i=2;i<m;i++){
        for(f[i]=r>i?min(r-i,f[p*2-i]):1;s[i-f[i]]==s[i+f[i]];f[i]++);
        if(i+f[i]>r)r=i+f[i],p=i;
      }
      for(i=1;i<n;i++)g[i]=f[i<<1|1]>>1;
      for(pre[0]=i=1;i<n;i++){
        j=pre[i-1];
        if(i-g[i]-1>=0)j-=pre[i-g[i]-1];
        j=!!j;
        pre[i]=pre[i-1]+j;
      }
      ll ret=pre[n-1];
      for(suf[n]=1,i=n-1;i;i--){
        j=suf[i+1];
        if(i+g[i]+1<=n)j-=suf[i+g[i]+1];
        j=!!j;
        if(j)ret+=pre[i-1];
        suf[i]=suf[i+1]+j;
      }
      return ret;
    }
    int main(){
      scanf("%d%d",&n,&m);
      for(i=1;i<=n;i++){
        scanf("%s",ch+1);
        for(j=1;j<=m;j++){
          a[i]=a[i]*233+ch[j];
          b[j]=b[j]*233+ch[j];
        }
      }
      printf("%lld",solve(a,n)*solve(b,m));
    }
    

      

    H. Qnp

    留坑。

    I. Salaj

    最优策略下,一定是$1$到$j$作为一个SCC,$j+1$到$n$每个点作为一个SCC,然后$1$到$j$用了$k$个环才得到。

    那么任意情况下,不影响SCC情况的多余边数不能超过$frac{n(n-1)+j(j-1)}{2}$。

    设$f[i][j][k]$表示考虑了$i$条边,$1$到$j$作为一个SCC,产生它用了$k$个环的方案数。

    转移则是要么将$j$往后扩,要么将当前边作为多余边,可以通过$i,j$和$k$很方便地得到可用边数。

    时间复杂度$O(n^3m)$,常数很小,可以通过,但可以通过前缀和优化到$O(n^2m)$。

    #include<cstdio>
    const int N=55,M=55*55;
    int Case,n,m,P,i,j,k,x,y,f[M][N][N],lim[N],ans[M];
    inline void up(int&a,int b){a=a+b<P?a+b:a+b-P;}
    int main(){
    	scanf("%d",&Case);
    	while(Case--){
    		scanf("%d%d%d",&n,&m,&P);
    		if(P==1){
    			for(i=1;i<=m;i++)printf("0 ");
    			puts("");
    			continue;
    		}
    		for(i=0;i<=m;i++)for(j=0;j<=n;j++)for(k=0;k<=n;k++)f[i][j][k]=0;
    		for(i=0;i<=n;i++)lim[i]=(n*(n-1)+i*(i-1))/2;
    		f[0][1][0]=1;
    		for(i=0;i<=m;i++)ans[i]=0;
    		for(i=0;i<=m;i++)for(j=1;j<=n;j++){
    			if(i>lim[j])continue;
    			for(k=0;k<=n;k++){
    				if(!f[i][j][k])continue;
    				up(ans[i],f[i][j][k]);
    				up(f[i+1][j][k],f[i][j][k]);
    				for(x=j+1;x<=n;x++){
    					if(i+1-k<x)break;
    					up(f[i+1][x][k+1],f[i][j][k]);
    				}
    			}
    		}
    		for(i=1;i<=m;i++)printf("%d ",ans[i]);
    		puts("");
    	}
    }
    

      

    J. Taxi

    若已经确定了所有车和人的位置,则对于树边来说,经过它的对数为两侧车和人数量的较小值。

    故枚举每条树边,再枚举一侧的边数,对于另一侧的人数前缀和加速统计贡献即可。

    时间复杂度$O(n^2)$。

    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    #include<string>
    #include<ctype.h>
    #include<math.h>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    #include<bitset>
    #include<algorithm>
    #include<time.h>
    using namespace std;
    void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
    #define MS(x, y) memset(x, y, sizeof(x))
    #define ls o<<1
    #define rs o<<1|1
    typedef long long LL;
    typedef unsigned long long UL;
    typedef unsigned int UI;
    template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
    template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
    const int N = 2520, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
    template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
    int casenum, casei;
    int n, m;
    char s[N][N];
    vector< pair<int,int> >a[N];
    int sz[N];
    void getsz(int x, int fa)
    {
    	sz[x] = 1;
    	for(auto it : a[x])if(it.first != fa)
    	{
    		int y = it.first;
    		getsz(y, x);
    		sz[x] += sz[y];
    	}
    }
    LL pw[N][N];
    LL qpow(LL x, LL p)
    {
    	return pw[x][p];
    	LL y = 1;
    	while(p)
    	{
    		if(p & 1)y = y * x % Z;
    		x = x * x % Z;
    		p>>=1;
    	}
    	return y;
    }
    LL c[2505][2505];
    LL C()
    {
    	c[0][0] = 1;
    	for(int i = 1; i <= 2500; ++i)
    	{
    		c[i][0] = 1;
    		for(int j = 1; j <= 2500; ++j)
    		{
    			c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % Z;
    		}
    	}
    }
    LL ans;
    LL sml[N][N], big[N][N];
    void init()
    {
    	for(int i = 0; i < N; ++i)
    	{
    		pw[i][0] = 1;
    		for(int j = 1; j < N; ++j)
    		{
    			pw[i][j] = pw[i][j - 1] * i % Z;
    		}
    	}
    	//peop
    	for(int i = 0; i <= n; ++i)
    	{
    		for(int j = 0; j <= m; ++j)
    		{
    			sml[i][j] = qpow(i, j) * c[m][j] % Z * qpow(n - i, m - j) % Z * j % Z;
    			if(j)gadd(sml[i][j], sml[i][j - 1]);
    			big[i][j] = qpow(i, j) * c[m][j] % Z * qpow(n - i, m - j) % Z % Z;
    		}
    		for(int j = m - 1; j >= 0; --j)
    		{
    			gadd(big[i][j], big[i][j + 1]);
    		}
    	}
    }
    LL cal(int peo_p, int car)
    {
    	int car_p = n - peo_p;
    	//情况一:peo <= car
    	//peo * qpow(peo_p, peo) * qpow(car_p, m - peo)  前缀和
    	
    	//情况二:peo > car
    	//car * qpow(peo_p, peo) * qpow(car_p, m - peo)  
    	
    	LL rtn = 0;
    	/*
    	for(int i = 0; i <= m; ++i)
    	{
    		gadd(rtn, qpow(peo_p, i) * c[m][i] % Z * qpow(car_p, m - i) % Z * min(i, car) % Z);
    	}
    	*/
    	rtn = (sml[peo_p][car] + car * big[peo_p][car + 1]) % Z;
    	return rtn;
    }
    void dfs(int x, int fa)
    {
    	for(auto it : a[x])if(it.first != fa)
    	{
    		int y = it.first;
    		dfs(y, x);
    		//枚举下面的车的数量
    		for(int botcar = 0; botcar <= m; ++botcar)
    		{
    			//得到上面的车的数量
    			int topcar = m - botcar;
    			
    			//考虑了路径的长度,考虑了车的放置方案数
    			LL tmp = it.second * qpow(sz[y], botcar) % Z * qpow(n - sz[y], topcar)  % Z * c[m][topcar] % Z;
    			
    			//gadd(ans, tmp * cal(sz[y], topcar) % Z);
    			gadd(ans, tmp * cal(n - sz[y], botcar) % Z);
    		}
    	}
    }
    
    int main()
    {
    	C();
    	while(~scanf("%d%d", &n, &m))
    	{
    		init();
    		for(int i = 1; i <= n; ++i)a[i].clear();
    		for(int i = 1; i < n; ++i)
    		{
    			int x, y, z;
    			scanf("%d%d%d", &x, &y, &z);
    			a[x].push_back({y,z});
    			a[y].push_back({x,z});
    		}
    		ans = 0;
    		getsz(1, 0);
    		//puts("before dfs");
    		dfs(1, 0);
    		printf("%lld
    ", ans * 2 % Z);
    	}
    	return 0;
    }
    /*
    【trick&&吐槽】
    5 2
    4 5 9805
    3 4 2001
    2 3 6438
    1 3 3790
    
    【题意】
    
    
    【分析】
    
    
    【时间复杂度&&优化】
    
    
    */
    

      

    K. Tris

    留坑。

    L. Xormites

    首先求出所有数的异或和$sum$,若先手拿到了$A$,则后手必然拿到了$sumoplus A$。

    若$sum=0$,则$A=sumoplus A$,必定平局。

    否则找到$sum$最高位的$1$,那么拿到奇数个$1$的一方获胜,可以将所有数转化为$0$和$1$。

    若$n$是偶数,那么将序列黑白染色,必定有一方异或和较大,且先手可以保证自己拿走全部黑或者全部白,故先手必胜。

    否则$n$是奇数,若$a_1$和$a_n$都是$0$,那么后手必然可以通过上述方法获胜。

    因此先手第一步必须要拿走一个$1$,接下来只能模仿对手行动,检查是否可能即可。

    时间复杂度$O(n)$。

    #include<cstdio>
    int Case,n,i,k,sum,a[50010];
    bool check(int L,int R){
      int l=L,r=R,i,cnt=0;
      while(l<r&&a[l]==a[r])l++,r--;
      for(i=l;i<=r;i+=2)if(a[i]^a[i+1])return 0;
      for(i=L;i<=R;i++)cnt+=a[i];
      return cnt/2%2==0;
    }
    int solve(){
      scanf("%d",&n);
      sum=0;
      for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
        sum^=a[i];
      }
      if(!sum)return 0;
      if(n%2==0)return 1;
      for(k=30;!(sum>>k&1);k--);
      for(i=1;i<=n;i++)a[i]=a[i]>>k&1;
      if(!a[1]&&!a[n])return -1;
      if(a[1]&&check(2,n))return 1;
      if(a[n]&&check(1,n-1))return 1;
      return -1;
    }
    int main(){
      scanf("%d",&Case);
      while(Case--){
        int t=solve();
        if(t==1)puts("First");
        if(t==0)puts("Draw");
        if(t==-1)puts("Second");
      }
    }
    

      

  • 相关阅读:
    [deviceone开发]-底部弹出选择
    [deviceone开发]-动态添加组件add方法的示例
    [deviceone开发]-openPage的动画效果示例
    [deviceone开发]-do_Album的简单示例
    [deviceone开发]-Star分享的几个示例
    [deviceone开发]-do_Webview的基本示例
    [deviceone开发]-do_Webview加载JQueryMobile的示例
    [deviceone开发]-心形点赞动画示例
    函数式编程思维学习 (1)
    android 14.04 64位 adb cannot run program adb
  • 原文地址:https://www.cnblogs.com/clrs97/p/8673379.html
Copyright © 2011-2022 走看看