Petrozavodsk Winter-2018. Jagiellonian U Contest

A. XOR

求出所有数的异或和$sum$,将所有数and上$sum$,然后求线性基,则选取$sum$的所有$1$对应的基最优。

时间复杂度$O(n\log x)$。

#include
#include
#include
#include
using namespace std;
typedef long long ll;
int Case,n,i,j;ll ans,sum,A,B,a[100],x,v[111111],pool[100];
const int N=20;
ll cal(){
	ll ans=sum;
	for(int S=0;S<1<>i&1)A^=v[i],B^=v[i];
		A-=B;
		if(A<0)A=-A;
		if(A>i&1);
	puts("");
}
ll myabs(ll x){return x>0?x:-x;}
ll solve1(int x){
	ll cur=a[x];
	for(int i=x-1;~i;i--)if(sum>>i&1){
		cur^=a[i];
	}
	return myabs(cur-(sum^cur));
}
ll solve2(int x){
	ll cur=0;
	for(int i=x-1;~i;i--){
		cur=max(cur,cur^a[i]);
	}
	return myabs(cur-(sum^cur));
}
int main(){
	//srand(time(NULL));
	scanf("%d",&Case);
	while(Case--){
		scanf("%d",&n);
		//n=rand()%10+1;
		sum=0;
		for(i=0;i<61;i++)a[i]=0;
		for(i=0;i>j&1){
				if(a[j])x^=a[j];
				else {a[j]=x;break;}
			}
		}
		
		//ll vio=cal();
		
		for(i=0;i<61;i++)for(j=i+1;j<61;j++)if(a[j]>>i&1)a[j]^=a[i];
		
		for(i=60;~i;i--)if(sum>>i&1)break;
		int lim=i;
		if(lim>=0){
			for(i=0;i<61;i++)pool[i]=a[i]&sum,a[i]=0;
			for(i=0;i<61;i++){
				x=pool[i];
				for(j=60;~j;j--)if(x>>j&1){
					if(a[j])x^=a[j];
					else {a[j]=x;break;}
				}
			}
			for(i=0;i<61;i++)for(j=i+1;j<61;j++)if(a[j]>>i&1)a[j]^=a[i];
			ans=min(sum,min(solve1(lim),solve2(lim)));
		}else{
			ans=0;
		}
		
		/*A=sum,B=0;*/
		
		//print(sum);
		//for(i=60;~i;i--)if(a[i])print(a[i]);
		
		/*ans=sum;
		for(i=60;~i;i--)if((A^a[i])>=(B^a[i])&&(A^a[i])-(B^a[i])

  

B. Tribute

按题意模拟即可。

#include
using namespace std;
int casenum, casei;
typedef unsigned int UI;
int n;
vectorvt, wt;
multisetsot;
multisetans;
bool solve()
{
	for(int i = 1; i <= n; ++i)
	{
		int x = *sot.begin();
		ans.insert(x);
//		printf("x = %d\n", x);
//		for(auto y : vt)printf("%d ", y); puts("");
		wt.clear();
		for(auto y : vt)
		{
			int z = x + y;
//			printf("%d\n", z);
			if(sot.find(z) == sot.end())return 0;
			sot.erase(sot.find(z));
			wt.push_back(z);
		}
		for(auto y : wt)vt.push_back(y);
	}
	return 1;
}
int main()
{
	scanf("%d", &casenum);
	for(casei = 1; casei <= casenum; casei ++)
	{
		scanf("%d", &n);
		int m = (1 << n) - 1;
		vt.clear(); vt.push_back(0);
		sot.clear();
		ans.clear();
		for(int i = 1; i <= m; ++i)
		{
			int x; scanf("%d", &x);
			sot.insert(x);
		}
		if(!solve())puts("NO");
		else
		{
			int id = 0;
			for(auto it : ans)printf("%d%c", it, ++id == n ? '\n' : ' ');
		}
	}
}

  

C. Boardroom Meeting

CDQ分治+扫描线树状数组,时间复杂度$O(n\log^2n)$。

#include
#include
using namespace std;
const int N=200010;
int Case,n,i,a[N],b[N],c[N],ans,f[N],qa[N],qb[N],bit[N],vis[N],T;
inline void up(int&a,int b){a>1,i,j,ca=0,cb=0;
	solve(l,mid);
	for(i=l;i<=mid;i++)qa[++ca]=i;
	for(;i<=r;i++)qb[++cb]=i;
	sort(qa+1,qa+ca+1,cmp);
	sort(qb+1,qb+cb+1,cmp);
	T++;
	for(i=j=1;i<=cb;i++){
		while(j<=ca&&a[qa[j]]

  

D. Secret Santa

第二类斯特林数容斥,时间复杂度$O(a\log n)$。

#include
const int N=2010,P=1000000007;
int n,a,i,j,Case,fac[N],S[N][N];
int po(int a,int b){int t=1;for(;b;b>>=1,a=1LL*a*a%P)if(b&1)t=1LL*t*a%P;return t;}
int T(int m,int n){
  int ans=0;
  for(int k=0;k=1&&j==0){S[i][j]=0;continue;}
    if(i==j){S[i][j]=1;continue;}
    if(j>=1&&j

  

E. Guessing Game

状压DP,设$f[S]$表示$S$情况下最优策略最坏需要的步数,其中$S$是个$k$位$3$进制数,分别表示每一位未询问、已询问且回答为$0$、已询问且回答为$1$的情况。

用高维前缀和预处理出所有$f[S]=0$的状态,剩下的状态枚举询问哪一位转移即可。

时间复杂度$O(3^kk)$。

#include
#include
using namespace std;
const int N=13,M=1600000;
int p[20],Case,n,m,i,j,k,f[M],c[M];char s[20];
inline int get(int x,int y){return x/p[y]%3;}
int main(){
	for(p[0]=i=1;i<20;i++)p[i]=p[i-1]*3;
	scanf("%d",&Case);
	while(Case--){
		scanf("%d%d",&n,&k);
		m=p[k];
		for(i=0;i

  

F. Flat Earth

按题意模拟即可。

#include
#include

int casenum, casei, a[10];
const double PI = acos(-1.0);

int main()
{
	scanf("%d", &casenum);
	for(casei = 1; casei <= casenum; casei ++){
		for(int i = 1; i <= 8; i ++){
			scanf("%d", &a[i]);
		}
		printf("%.10f\n", PI * a[4] * a[4]);
	}
}

  

G. We Need More Managers!

建立一张$2^n$个点的图,分别表示每种串。对于每个点,向恰好改变了一位的$n$个点连边。

则题意可转化为对给定$m$个点求出两两最短路作为权值的最小生成树。

设$p_x$和$d_x$分别表示离每个点最近的给定串以及对应的距离,可以BFS求出,则对于一条边$(u,v)$,在生成树中加入一条连接$p_u$和$p_v$,权值为$d_u+d_v+1$的边即可。

时间复杂度$O(n2^n\alpha(m))$。

#include
#include
using namespace std;
const int N=1100000;
int Case,n,m,all,i,j,a[N];char s[100];
int h,t,d[N],p[N],x,y,z,q[N];
int f[N],ans,ce;
struct E{
	int x,y,z;
	E(){}
	E(int _x,int _y,int _z){x=_x,y=_y,z=_z;}
}e[N*20];
inline bool cmp(const E&a,const E&b){return a.z

  

H. Masterpiece

若不考虑每个点是被第几次走过的,则方案唯一,$O(n)$模拟求出方案后统计分岔点个数$t$,则答案为$2^t$。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
void fre() {  }
#define MS(x, y) memset(x, y, sizeof(x))
#define rt 1,1,n
#define ls o<<1
#define rs o<<1|1
#define mid (l+r>>1)
#define lson ls,l,mid
#define rson rs,mid+1,r
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 1e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n;
int r[N], c[N];
//try go to (y, x)
bool flag = 1;
int r1, r2;
int c1, c2;
bool doit(int y, int x)
{
    if(r1 == r2 && c1 == c2)return flag = 1;
    if(y > n || x > n)return 0;
    if(!r[y] || ! c[x])return 0;
    --r[y];
    --c[x];
    return flag = 1;
}
int solve()
{
    int ans = 1;
    r1 = 1, c1 = 1;
    r2 = 1, c2 = 1;
    doit(1, 1); --r[1]; --c[1];
    while(1)
    {
        flag = 0;
        if(r1 == r2 && c1 == c2)
        {
            if(r1 == n && c1 == n)
            {
                return ans;
            }
            int rnum = r[r1];
            int cnum = c[c1];
            if(rnum && cnum)
            {
                ans = ans * 2 % Z;
                for(int i = 1; i <= rnum; ++i)
                {
                    if(!doit(r1, ++c1))return 0;
                }
                for(int i = 1; i <= cnum; ++i)
                {
                    if(!doit(++r2, c2))return 0;
                }
            }
            else if(rnum)
            {
                if(!doit(r1, ++c1))return 0;
                ++c2;
            }
            else if(cnum)
            {
                if(!doit(++r1, c1))return 0;
                ++r2;
            }
            else return 0;
        }
        while(r1 < r2)
        {
            if(r[r1])
            {
                if(!doit(r1, ++c1))return 0;
            }
            else
                if(!doit(++r1, c1))return 0;
        }
        while(r2 < r1)
        {
            if(r[r2])
            {
                if(!doit(r2, ++c2))return 0;
            }
            else
                if(!doit(++r2, c2))return 0;
        }
        while(c1 < c2)
        {
            if(c[c1])
            {
                if(!doit(++r1, c1))return 0;
            }
            else
                if(!doit(r1, ++c1))return 0;
        }
        while(c2 < c1)
        {
            if(c[c2])
            {
                if(!doit(++r2, c2))return 0;
            }
            else
                if(!doit(r2, ++c2))return 0;
        }
        if(!flag)return 0;
    }
}
int main()
{
	scanf("%d", &casenum);
	for(casei = 1; casei <= casenum; ++casei)
	{
        scanf("%d", &n);
        //int n = rand() % 10 + 1;
        for(int i = 1; i <= n; ++i)
        {
            scanf("%d", &r[i]);
            //r[i] = rand() % (n + 1);
        }
        for(int i = 1; i <= n; ++i)
        {
            scanf("%d", &c[i]);
            //c[i] = rand() % (n + 1);
        }
        int ans = solve();
        for(int i = 1; i <= n; ++i)
        {
            if(r[i] || c[i])ans = 0;
        }
        printf("%d\n", ans);
	}
	return 0;
}

/*
【trick&&吐槽】


【题意】


【分析】


【时间复杂度&&优化】

100
5
3 3 3 3 3
1 4 4 3 3

4
4 2 2 4
4 2 2 4

*/

  

I. Don’t Split The Atom!

胜负只取决于$n$的奇偶性。

#include
#include

int casenum, casei, a[10];
const double PI = acos(-1.0);

int main()
{
	scanf("%d", &casenum);
	for(casei = 1; casei <= casenum; casei ++){
		int n;
		scanf("%d", &n);
		puts(n & 1 ? "B" : "A");
	}
}

  

J. Bobby Tables

组合数可以看作长度为$k$的一个区间除以长度为$k$的一个前缀。

枚举每个$k$作为前缀,二分对应的区间,取对数加速判定,在附近配合取模判定即可。

时间复杂度$O(m\log m)$。

#include
#include
#include
using namespace std;
typedef long double ld;
const int N=200010,P=1000000007,K=20;
const ld eps=1e-2;
int Case,n,m,i,a[N],fac[N],inv[N],mul;
ld Log[N],s[N],sum;
inline bool check(int n,int m){
	ld A=s[n]-s[n-m]-s[m];
	if(fabs(A-sum)>eps)return 0;
	int B=1LL*fac[n]*inv[n-m]%P*inv[m]%P;
	if(B!=mul)return 0;
	puts("YES");
	printf("%d %d\n",n,m);
	return 1;
}
inline void solve(){
	scanf("%d%d",&n,&m);
	sum=0;
	mul=1;
	for(i=1;i<=n;i++){
		scanf("%d",&a[i]);
		mul=1LL*mul*a[i]%P;
		sum+=Log[a[i]];
	}
	for(i=1;i<=m;i++){
		//[x,x+i-1]/[1..i]
		//C(x+i-1,i)
		//x+i-1>=i
		//1<=x<=m+1-i
		//x+i-1<=m
		int l=1,r=m+1-i;
		while(l<=r){
			int mid=(l+r)>>1;
			ld A=s[mid+i-1]-s[mid-1]-s[i];
			if(fabs(A-sum)

  

K. Triples

长链剖分或树分治经典题。

#include
#include
#include
#include
#define pb push_back
#define V vector
#define N 200010
using namespace std;
typedef long long ll;
typedef pairP;
int Case;
int n,i,x,y,g[N],v[N<<1],nxt[N<<1],ed,f[N],del[N];ll ans;V

h[N]; inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} inline bool cmp(V*a,V*b){return a->size()>b->size();} templateinline C&get(V&a,size_t x){return a[a.size()-x-1];} V*dfs(int x,int y){ V*>t; for(int i=g[x];i;i=nxt[i])if(v[i]!=y)t.pb(dfs(v[i],x)); if(!t.size())return new V(1,1); sort(t.begin(),t.end(),cmp); V*a=t[0]; a->pb(1); if(t.size()==1)return a; Vb; b.resize(t[1]->size()+1,0); for(int i=1;i*u=t[i]; for(int j=1;j<=u->size();j++){ int ch=get(*u,j-1),&rt=get(*a,j);ll&rd=get(b,j); ans+=1LL*ch*rd; rd+=1LL*ch*rt; rt+=ch; } } for(int i=1;in){ y=x,x=v[i],t=1; break; } }while(t); return x; } inline void work(V >&d,V >&q,int k,bool rt,int x){ int st=k==1?0:d.size()-1,en=k==1?d.size():-1;Va(1,rt); for(int i=st;i!=en;i+=k){ VD=d[i]; for(V

::iterator p=q[i].begin();p!=q[i].end();p++){ int j=p->first; if(jsecond; } a.resize(max(a.size(),D.size()),0); for(int j=0;j::iterator p=h[x].begin();pfirst; if(jsecond; } } void dfsd(int x,int y,int z,V&d){ if(z==d.size())d.pb(1);else d[z]++; for(int i=g[x];i;i=nxt[i])if(v[i]!=y&&!del[v[i]])dfsd(v[i],x,z+1,d); } void dfsq(int x,int y,int z,V

&q){ for(V

::iterator i=h[x].begin();ifirst-z; if(t>=0)q.pb(P(t,i->second)); } for(int i=g[x];i;i=nxt[i])if(v[i]!=y&&!del[v[i]])dfsq(v[i],x,z+1,q); } void solve(int x){ int y=findroot(x); del[y]=1; for(int i=g[y];i;i=nxt[i])if(!del[v[i]])solve(v[i]); V >d;V >q; for(int i=g[y];i;i=nxt[i])if(!del[v[i]]){ VA(1,0); dfsd(v[i],-1,1,A); d.pb(A); V

B; dfsq(v[i],-1,1,B); q.pb(B); } work(d,q,1,1,y),work(d,q,-1,0,y); del[y]=0; } int main(){ scanf("%d",&Case); while(Case--){ i=x=y=0; memset(g,0,sizeof g); memset(f,0,sizeof f); memset(v,0,sizeof v); memset(nxt,0,sizeof nxt); ed=0; memset(del,0,sizeof del); ans=0; for(i=0;i

  

L. Related Languages

枚举$O(nm)$对区间右端点,左端点的取值满足单调性,双指针即可。

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

#include
const int N=4010;
int Case,n,m,i,j,k,ans,g[N*3];
char a[N],b[N],f[N][N];
int w[N*3],V;
int main(){
  scanf("%d",&Case);
  while(Case--){
    scanf("%d%d%d%s%s",&n,&m,&V,a+1,b+1);
    ans=0;
    for(i=1;i<=n;i++)for(j=1;j<=m;j++)f[i][j]=a[i]!=b[j];
    for(i=0;i<=N+N+5;i++)g[i]=w[i]=0;
    for(i=1;i<=n;i++)for(j=1;j<=m;j++){
      k=i-j+N;
      g[k]++;
      w[k]+=f[i][j];
      int G=g[k],W=w[k];
      while(G>0&&W>V){
        G--;
        W-=f[i-G][j-G];
      }
      g[k]=G;
      w[k]=W;
      if(G>ans)ans=G;
    }
    printf("%d\n",ans);
  }
}

  

你可能感兴趣的:(Petrozavodsk Winter-2018. Jagiellonian U Contest)