Codeforces Round #534 Div. 1
A:用一列放竖着的方块,两列放横着的方块。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 1010 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n; char s[N]; signed main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); #endif scanf("%s",s+1);n=strlen(s+1); int x=0,y=0; for (int i=1;i<=n;i++) if (s[i]=='0') { x++; if (x&1) cout<<1<<' '<<1<<endl; else cout<<3<<' '<<1<<endl; } else { y++;if (y>4) y=1; cout<<y<<' '<<2<<endl; } return 0; //NOTICE LONG LONG!!!!! }
B:先询问2k 2k+1,假设已经知道了a>2k,则当a>2k+1时,后者大,否则前者大。这样可以倍增出一个a的取值区间。注意到在区间内x mod a值开始单增,中间突变为0(即x=a),然后又单增,并且最开始的值要比最终的值大。于是拿2k+1和一个二分值比较即可。注意特判a=1的情况,询问0和1即可。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } char s[10]; signed main() { cin>>(s+1); while (s[1]!='e') { cout<<'?'<<' '<<0<<' '<<1<<endl; char c;cin>>c; if (c=='x') cout<<'!'<<' '<<1<<endl; else { int x; for (int i=0;i<30;i++) { cout<<'?'<<' '<<(1<<i)<<' '<<(1<<i+1)<<endl; cin>>c; if (c=='x') {x=i;break;} } int y=(1<<x+1); int l=(1<<x)+1,r=y-1,ans=y; while (l<=r) { int mid=l+r>>1; cout<<'?'<<' '<<mid<<' '<<y<<endl; cin>>c; if (c=='x') l=mid+1; else ans=mid,r=mid-1; } cout<<'!'<<' '<<ans<<endl; } cin>>(s+1); } return 0; //NOTICE LONG LONG!!!!! }
C:二选一当然是考虑问题间有什么关系。随便找一棵dfs树,如果深度>=n/k,那么显然有一条长度为n/k的简单路径;否则由抽屉原理,至少有一层的点数超过k,由此可得树的叶子数量超过k。注意到由于每个点度数至少为3,叶子至少会往上连两条边。我们考虑对每个叶子构造一个以其为标记的环。唯一的要求是环长不能为3的倍数,由于我们有至少两条边,所以可以得到至少三个包含该叶子的环,容易发现其中一定存在一个满足要求的。并且由于环长不会超过树的深度,所以总输出量也是O(n)的。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<map> #include<cassert> using namespace std; #define ll long long #define N 500010 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,k,p[N],deep[N],fa[N],t,root=1; bool flag[N],isleaf[N]; struct data{int to,nxt; }edge[N<<1]; void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;} void dfs(int k) { flag[k]=1;int son=0; for (int i=p[k];i;i=edge[i].nxt) if (!flag[edge[i].to]) { deep[edge[i].to]=deep[k]+1; fa[edge[i].to]=k; son++; dfs(edge[i].to); } if (son==0) isleaf[k]=1; } signed main() { #ifndef ONLINE_JUDGE freopen("c.in","r",stdin); freopen("c.out","w",stdout); #endif n=read(),m=read(),k=read(); for (int i=1;i<=m;i++) { int x=read(),y=read(); addedge(x,y),addedge(y,x); } deep[root]=1;dfs(root); for (int i=1;i<=n;i++) if (deep[i]>=n/k+(n%k>0)) { cout<<"PATH"<<endl;cout<<deep[i]<<endl; for (int x=i;x!=root;x=fa[x]) printf("%d ",x); cout<<root; return 0; } cout<<"CYCLES"<<endl; for (int i=1;i<=n;i++) if (isleaf[i]) { bool f=0; for (int j=p[i];j;j=edge[j].nxt) if (edge[j].to!=fa[i]&&(deep[i]-deep[edge[j].to])%3!=2) { printf("%d\n",deep[i]-deep[edge[j].to]+1); for (int x=i;x!=edge[j].to;x=fa[x]) printf("%d ",x); printf("%d\n",edge[j].to); f=1;break; } if (!f) { int u=0,v=0; for (int j=p[i];j;j=edge[j].nxt) if (edge[j].to!=fa[i]) if (!u) u=edge[j].to;else if (!v) v=edge[j].to; else break; if (deep[u]<deep[v]) swap(u,v); printf("%d\n",deep[u]-deep[v]+2); printf("%d ",i); while (u!=v) printf("%d ",u),u=fa[u]; printf("%d\n",v); } k--;if (k==0) break; } return 0; //NOTICE LONG LONG!!!!! }
D:先求出所有数的gcd,显然每次对一个数操作都应该去除gcd至少一种质因子,给每个数只保留这些质因子。由于值域1e12,不同的质因子数量最多为11个,设该数为m,那么所需要操作的数也不会超过m个。显然可以得到一个dp,即f[i][j][k]为前i个数操作j个质因子状态为k的最小Σe。复杂度O(n·m·3m)。
注意到对于这m种质因子次数均相同的数,只需要保留代价最小的m个。于是先去一下重,猜想去重之后剩下数的数量就不会特别多。但乘上m·3m后还是跑不动。进一步发现对于均能去除某一质因子集合的数,也只需要保留代价最小的m个。这样继续暴力去重,剩下数的数量不超过m·2m,并且同时我们可以求出每个数可以用于去除哪些集合,总集合数量是m·2m的。回到原dp中可以发现复杂度就变成了O(m2·3m)。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<queue> #include<vector> using namespace std; #define ll long long #define N 1000010 #define M 11 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} ll gcd(ll n,ll m){return m==0?n:gcd(m,n%m);} ll read() { ll x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,cnt[M],t,LG2[1<<M]; ll m,k,p[M],pw[M][100],tot[1<<M],f[M][1<<M]; bool flag[N]; struct data { int v;ll x; bool operator <(const data&a) const { return x<a.x; } }a[N],b[N]; vector<int> s[N]; priority_queue<data> q[1<<M]; bool cmp(const data&a,const data&b) { return a.v<b.v; } signed main() { #ifndef ONLINE_JUDGE freopen("d.in","r",stdin); freopen("d.out","w",stdout); #endif n=read(),k=read(); for (int i=1;i<=n;i++) m=gcd(m,a[i].x=read()); for (int i=1;i<=n;i++) a[i].v=read(); for (ll i=2;i*i<=m;i++) if (m%i==0) { p[t++]=i; while (m%i==0) m/=i; } if (m>1) p[t++]=m;if (t==0) {cout<<0;return 0;} for (int i=0;i<t;i++) { pw[i][0]=1; for (int j=1;j<100;j++) pw[i][j]=pw[i][j-1]*p[i]; } sort(a+1,a+n+1); int u=0; for (int i=1;i<=n;i++) { int v=i; while (a[v+1].x==a[i].x) v++; sort(a+i,a+v+1,cmp); for (int j=1;j<=min(t,v-i+1);j++) b[++u]=a[i+j-1]; i=v; } swap(a,b);n=u; for (int i=0;i<M;i++) LG2[1<<i]=i; for (int j=1;j<(1<<t);j++) for (int i=0;i<t;i++) q[j].push((data){0,100000000000000ll}); for (int i=1;i<=n;i++) { tot[0]=1; ll u=a[i].x;memset(cnt,0,sizeof(cnt)); for (int j=0;j<t;j++) while (u%p[j]==0) cnt[j]++,u/=p[j]; for (int j=1;j<(1<<t);j++) { tot[j]=tot[j^(j&-j)]*pw[LG2[j&-j]][cnt[LG2[j&-j]]]; if (tot[j]<=k&&a[i].v<q[j].top().x) q[j].pop(),q[j].push((data){i,a[i].v}); } } for (int j=1;j<(1<<t);j++) while (!q[j].empty()) { data x=q[j].top();q[j].pop(); s[x.v].push_back(j); } memset(f,42,sizeof(f));f[0][0]=0; for (int i=1;i<=n;i++) for (int j=t-1;j>=0;j--) { for (int k=0;k<s[i].size();k++) { int x=s[i][k]; f[j+1][x]=min(f[j+1][x],f[j][0]+a[i].v); for (int y=(1<<t)-1^x;y;y=y-1&((1<<t)-1^x)) f[j+1][x|y]=min(f[j+1][x|y],f[j][y]+a[i].v); } } ll ans=1000000000000000ll; for (int i=1;i<=t;i++) if (f[i][(1<<t)-1]<ans) ans=min(ans,i*f[i][(1<<t)-1]); if (ans>=1000000000000000ll) cout<<-1; else cout<<ans; return 0; //NOTICE LONG LONG!!!!! }
E感觉过于神仙。