tozangezan's diary

勝手にソースコードをコピペして利用しないでください。

Codeforces Round #363 (Div. 1)

一体何をやっているんですかね......

A B C D E F Place
00:031 00:21 00:39 (+3) 02:07 (+7) - - 26th

A:
Div1...?

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
int b[110];
int dp[110][3];
int main(){
	int a;scanf("%d",&a);
	for(int i=0;i<a;i++)scanf("%d",b+i);
	for(int i=0;i<110;i++)for(int j=0;j<3;j++)dp[i][j]=mod;
	dp[0][0]=0;
	for(int i=0;i<a;i++){
		for(int j=0;j<3;j++){
			if(b[i]/2&&j!=1){
				dp[i+1][1]=min(dp[i+1][1],dp[i][j]);
			}
			if(b[i]%2&&j!=2){
				dp[i+1][2]=min(dp[i+1][2],dp[i][j]);
			}
			dp[i+1][0]=min(dp[i+1][0],dp[i][j]+1);
		}
	}
	int ret=mod;
	for(int i=0;i<3;i++)ret=min(ret,dp[a][i]);
		printf("%d\n",ret);
}

B:
大きさ1のサイクルが1つあるのが理想。大きさ1のサイクルが複数あるとき、大きさ2以上のサイクルしかないとき等を考えればよい。
visを1と2で埋めていくサイクル探しはトポロジカルソートでもおなじみ(なのか?)。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
int p[210000];
int v[210000];
int main(){
	int a;scanf("%d",&a);
	for(int i=0;i<a;i++){
		scanf("%d",p+i);p[i]--;
	}
	int ret=0;
	int par=-1;
	int sec=-1;
	for(int i=0;i<a;i++){
		if(v[i])continue;
		int at=i;
		vector<int>vis;
		v[at]=2;
		vis.push_back(at);
		while(1){
			int to=p[at];
			if(v[to]==1)break;
			if(v[to]==2){
				if(to!=at){
					ret++;
					p[at]=-1;
					sec=at;
				}else{
					if(!~par){
						par=at;
					}else{
						ret++;
						p[at]=par;
					}
				}
				break;
			}
			v[to]=2;
			at=to;
			vis.push_back(to);
		}
		for(int j=0;j<vis.size();j++){
			v[vis[j]]=1;
		}
	}
	for(int i=0;i<a;i++){
		if(p[i]==-1){
			if(~par)p[i]=par;
			else p[i]=sec;
		}
	}
	printf("%d\n",ret);
	for(int i=0;i<a;i++){
		if(i)printf(" ");
		printf("%d",p[i]+1);
	}
	printf("\n");
}

C:
普通にbitDPやるだけなのに、なぜかp=0のケースが存在していてHackし放題になっている。
設定として不自然だと思うんだけどなあ...

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
double dp[1<<20];
double p[22];
double q[22];
double ret[22];
int ind[22];
double EPS=1e-9;
int main(){
	int n,b;scanf("%d%d",&n,&b);
	for(int i=0;i<n;i++){
		scanf("%lf",q+i);
	}
	int a=0;
	for(int i=0;i<n;i++){
		if(q[i]>EPS){
			p[a++]=q[i];
			ind[a-1]=i;
		}
	}
	b=min(b,a);
	dp[0]=1;
	for(int i=0;i<(1<<a)-1;i++){
		double tot=1;
		for(int j=0;j<a;j++){
			if(i&(1<<j))tot-=p[j];
		}
		if(tot<EPS)continue;
		for(int j=0;j<a;j++){
			if(i&(1<<j))continue;
			dp[i+(1<<j)]+=dp[i]*p[j]/tot;
		}
	}
	for(int i=0;i<(1<<a);i++){
		if(__builtin_popcount(i)!=b)continue;
		for(int j=0;j<a;j++)if(i&(1<<j))ret[ind[j]]+=dp[i];
	}
	for(int j=0;j<n;j++){
		if(j)printf(" ");
		printf("%.12f",ret[j]);
	}
	printf("\n");
}

D:
各獲物から各ワープ地点まで辺を張ったときに、辺上に来るものを全部持っておく(ただし、k個以上が邪魔しているときは、絶対到達不可能なので無視する)。
ゴールから(到達したい頂点集合,残り使えるワープ先の集合)を頂点にdfsしていくと、意外とこれは速くて間に合う。(行きたい頂点の集合は一瞬で増えるか一瞬で消えるのではなかろうか)

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
const long double EPS = 1e-20;
const long double INF = 1e+20;
const long double PI = acos(-1);
int sig(long double r) { return (r < -EPS) ? -1 : (r > +EPS) ? +1 : 0; }
inline long double ABS(long double a){return max(a,-a);}
struct Pt {
	long double x, y;
	Pt() {}
	Pt(long double x, long double y) : x(x), y(y) {}
	Pt operator+(const Pt &a) const { return Pt(x + a.x, y + a.y); }
	Pt operator-(const Pt &a) const { return Pt(x - a.x, y - a.y); }
	Pt operator*(const Pt &a) const { return Pt(x * a.x - y * a.y, x * a.y + y * a.x); }
	Pt operator-() const { return Pt(-x, -y); }
	Pt operator*(const long double &k) const { return Pt(x * k, y * k); }
	Pt operator/(const long double &k) const { return Pt(x / k, y / k); }
	long double ABS() const { return sqrt(x * x + y * y); }
	long double abs2() const { return x * x + y * y; }
	long double arg() const { return atan2(y, x); }
	long double dot(const Pt &a) const { return x * a.x + y * a.y; }
	long double det(const Pt &a) const { return x * a.y - y * a.x; }
};
long double tri(const Pt &a, const Pt &b, const Pt &c) { return (b - a).det(c - a); }

int iSP(Pt a, Pt b, Pt c) {
	int s = sig((b - a).det(c - a));
	if (s) return s;
	if (sig((b - a).dot(c - a)) < 0) return -2; // c-a-b
	if (sig((a - b).dot(c - b)) < 0) return +2; // a-b-c
	return 0;
}
int iLL(Pt a, Pt b, Pt c, Pt d) {
	if (sig((b - a).det(d - c))) return 1; // intersect
	if (sig((b - a).det(c - a))) return 0; // parallel
	return -1; // correspond
}
bool iLS(Pt a, Pt b, Pt c, Pt d) {
	return (sig(tri(a, b, c)) * sig(tri(a, b, d)) <= 0);
}
bool iSS(Pt a, Pt b, Pt c, Pt d) {
	return (iSP(a, b, c) * iSP(a, b, d) <= 0 && iSP(c, d, a) * iSP(c, d, b) <= 0);
}
bool iSSstrict(Pt a, Pt b, Pt c, Pt d) {
	return (sig(tri(a, b, c)) * sig(tri(a, b, d)) < 0 && sig(tri(c, d, a)) * sig(tri(c, d, b)) < 0);
}
Pt p[10];
Pt q[1100];
int num[1100][7][7];
pair<long double,int> tmp[1100];
int m;
int solve(set<int>a,int b){
	if(a.size()>__builtin_popcount(b))return 0;
	if(a.size()==0)return 1;
//	for(set<int>::iterator it=a.begin();it!=a.end();it++)printf("%d ",*it);
//	printf(": %d\n",b);
	for(set<int>::iterator it=a.begin();it!=a.end();it++){

		int at=*it;
		for(int i=0;i<m;i++){
			if(b&(1<<i)){
				if(num[at][i][0]==-2)continue;
				int tb=b-(1<<i);
				set<int>to=a;
				to.erase(at);
				for(int j=0;j<m;j++){
					if(num[at][i][j]==-1){
						break;
					}
					to.insert(num[at][i][j]);
				}
				if(solve(to,tb))return 1;
			}
		}
	}
	return 0;
}
int main(){
	int a,b;scanf("%d%d",&a,&b);
	m=a;
	for(int i=0;i<a;i++){
		int X,Y;scanf("%d%d",&X,&Y);
		p[i]=Pt(X,Y);
	}
	for(int i=0;i<b;i++){
		int X,Y;scanf("%d%d",&X,&Y);
		q[i]=Pt(X,Y);
	}

	for(int i=0;i<b;i++)for(int j=0;j<a;j++)for(int k=0;k<a;k++)num[i][j][k]=-1;
	for(int i=0;i<a;i++){
		for(int j=0;j<b;j++){
			int sz=0;
			for(int k=0;k<b;k++){
				if(j==k)continue;
				if(iSP(p[i],q[k],q[j])==2){
		//			printf("%d %d %d\n",i,j,k);
				//	if(num[j][i][0]==-1||iSP(q[j],q[k],q[num[j][i][0]])==2){
//
//						num[j][i][0]=k;
//					}
					tmp[sz++]=make_pair((q[j]-q[k]).abs2(),k);
				}
			}
			if(sz>=a){
				num[j][i][0]=-2;
			}else{
				for(int k=0;k<sz;k++){
					num[j][i][k]=tmp[k].second;
				}
			}
	//		printf("%d %d: %d\n",j,i,num[j][i][0]);
		}
	}
	int ret=0;
	for(int i=0;i<b;i++){
		set<int>st;
		st.insert(i);
		if(solve(st,(1<<a)-1))ret++;
	//	printf("\n");
	}
	printf("%d\n",ret);
}

E:
OpenCupでも結構あるけど、「見た目は面倒なだけの実装問題だが、実は細かいところがやけに難しい問題」なのではなかろうか。

F:
あとでやろう。

Codeforces Round #364 (Div. 1)

遅いのとA,B逆なことを除けばうまくいった?

A B C D E Place
00:26 00:33 01:28 - - 20th

A:
全然わかりませんでした(オイ
バス乗車時間を二分探索。式もなんだか難しい。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
int main(){
	int a,b,c,d,e;
	scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);
	double left=0;
	double right=(double)b/d;
	int r=(a+e-1)/e;
	for(int i=0;i<100;i++){
		double M=(left+right)/2;
		double at=0;
		double t=0;
		double x=0;
		for(int j=0;j<r;j++){
			t+=M;
			at+=M*d;
			if(j==r-1)break;
			x=t*c;
			double t2=(at-x)/(c+d);
			x+=t2*c;
			at=x;
			t+=t2;
		}
	//	printf("%f: %f %f\n",M,t,at);
		if(at<b){
			left=M;
		}else right=M;
	}
	//printf("%f\n",left);
	printf("%.12f\n",left+((double)b-left*d)/c);
}

B:
辺ごとに両端の少ない方だけ人が移動する。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
int v[210000];
vector<int>g[221000];
long long ret=0;
int sz[221000];
int M;
void dfs(int a,int b){
	for(int i=0;i<g[a].size();i++){
		if(b==g[a][i])continue;
		dfs(g[a][i],a);
		sz[a]+=sz[g[a][i]];
		ret+=min(sz[g[a][i]],M-sz[g[a][i]]);
	}
	if(v[a])sz[a]++;

}
int main(){
	int a,b;
	scanf("%d%d",&a,&b);
	M=b*2;
	for(int i=0;i<b*2;i++){
		int p;scanf("%d",&p);p--;v[p]++;
	}
	for(int i=0;i<a-1;i++){
		int p,q;scanf("%d%d",&p,&q);p--;q--;
		g[p].push_back(q);
		g[q].push_back(p);
	}
	dfs(0,-1);
	printf("%I64d\n",ret);
}

C:
まずDinic (計算量的にも問題なし)。流量0のときと3以上のときは自明。
流量が1,2いずれのときも1つの辺はフローで流れた辺を使う。2のときは残った辺もフローで流れた辺。
フローで使った辺を1つ選び、残りでs->tの移動中必ず通る辺(dfs情報のアレでチェック可能)を全部試せばよい。1のときはフローで使った辺を消したあとBFSしてs->tいけるかどうかも判断する。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
const int D_MAX_V=1002;
const int D_v_size=1002;
struct D_wolf{
	int t,c,r;
	int u,w;
	D_wolf(){t=c=r=u=w=0;}
	D_wolf(int t1,int c1,int r1,int u1,int w1){
		t=t1;c=c1;r=r1;u=u1;w=w1;
	}
};
vector<D_wolf>D_G[D_MAX_V];
int D_level[D_MAX_V];
int D_iter[D_MAX_V];

void add_edge(int from,int to,int cap,int id){
	D_G[from].push_back(D_wolf(to,cap,D_G[to].size(),1,id));
	D_G[to].push_back(D_wolf(from,0,D_G[from].size()-1,0,id));
}
void D_bfs(int s){
	for(int i=0;i<D_v_size;i++)D_level[i]=-1;
	queue<int> Q;
	D_level[s]=0;
	Q.push(s);
	while(Q.size()){
		int v=Q.front();
		Q.pop();
		for(int i=0;i<D_G[v].size();i++){
			if(D_G[v][i].c>0&&D_level[D_G[v][i].t]<0){
				D_level[D_G[v][i].t]=D_level[v]+1;
				Q.push(D_G[v][i].t);
			}
		}
	}
}
int D_dfs(int v,int t,int f){
	if(v==t)return f;
	for(;D_iter[v]<D_G[v].size();D_iter[v]++){
		int i=D_iter[v];
		if(D_G[v][i].c>0&&D_level[v]<D_level[D_G[v][i].t]){
			int d=D_dfs(D_G[v][i].t,t,min(f,D_G[v][i].c));
			if(d>0){
				D_G[v][i].c-=d;
				D_G[D_G[v][i].t][D_G[v][i].r].c+=d;
				return d;
			}
		}
	}
	return 0;
}
int max_flow(int s,int t){
	int flow=0;
	for(;;){
		D_bfs(s);
		if(D_level[t]<0)return flow;
		for(int i=0;i<D_v_size;i++)D_iter[i]=0;
		int f;
		while((f=D_dfs(s,t,99999999))>0){flow+=f;}
	}
	return 0;
}
const int MAXN=1100;
const int MAXM=61000;
int zeit, dis[MAXN], fin[MAXN], low[MAXN], par[MAXN], dep[MAXN];
int kodat[MAXN], koptr[MAXN + 1];
int zu[MAXM];
int ptr[MAXN];
int nxt[MAXM];
int n;
void dfsInfo(int u, int oy, int d) {
	dis[u] = low[u] = zeit++; par[u] = oy; dep[u] = d;
	int i, v;
	for (i = ptr[u]; ~i; i = nxt[i]) if ((v = zu[i]) != oy) {
		if (!~dis[v]) {
			dfsInfo(v, u, d + 1);
			low[u] = min(low[u], low[v]);
		} else {
			low[u] = min(low[u], dis[v]);
		}
	}
	fin[u] = zeit++;
}
void dfsInfos() {
	memset(dis, ~0, n * 4); zeit = 0;
	for (int u = 0; u < n; ++u) if (!~dis[u]) dfsInfo(u, -1, 0);
	for (int u = 0; u < n; ++u) {
		int &j = koptr[u + 1] = koptr[u];
		for (int i = ptr[u]; ~i; i = nxt[i]) if (u == par[zu[i]]) kodat[j++] = zu[i];
	}
}
bool produce(int u, int v) {
	return (dis[u] <= dis[v] && fin[u] >= fin[v]);
}
int related(int u, int v) {
	int s = koptr[u], e = koptr[u + 1], h;
	for (; s + 1 < e; ) {
		h = (s + e) >> 1;
		(dis[kodat[h]] <= dis[v]) ? s = h : e = h;
	}
	return kodat[s];
}
bool isBridge(int u, int v) {
	if (dis[u] > dis[v]) swap(u, v);
	return (u == par[v] && dis[v] <= low[v]);
}
bool isFatalEdge(int u, int v, int a, int b) {
	if (dis[u] > dis[v]) swap(u, v);
	return (u == par[v] && dis[v] <= low[v] && produce(v, a) != produce(v, b));
}
bool isFatalPoint(int u, int a, int b) {
	bool ua = produce(u, a), ub = produce(u, b);
	if (!ua && !ub) {
		return 0;
	} else if (ua && ub) {
		int ra = related(u, a), rb = related(u, b);
		return (ra != rb && (dis[u] <= low[ra] || dis[u] <= low[rb]));
	} else {
		if (ub) swap(a, b);
		return (dis[u] <= low[related(u, a)]);
	}
}
int vis[1100];
int x[31000];
int y[31000];
int z[31000];
int f[1100][1100];
vector<pair<int,int> > G[1100];
int main(){
	int a,b;
	scanf("%d%d",&a,&b);
	int s,t;scanf("%d%d",&s,&t);
	s--;t--;

	for(int i=0;i<b;i++){
		int p,q,r;scanf("%d%d%d",&p,&q,&r);p--;q--;
		f[p][q]++;
		f[q][p]++;
		x[i]=p;y[i]=q;z[i]=r;
		if(p==q)continue;
		add_edge(p,q,1,i);
		add_edge(q,p,1,i);
	}
	int ans=max_flow(s,t);
	if(ans>2){
		printf("-1\n");return 0;
	}
	if(ans==0){
		printf("0\n0\n");return 0;
	}
	for(int i=0;i<a;i++){
		for(int j=0;j<D_G[i].size();j++){
			if(D_G[i][j].u&&D_G[i][j].c==0){
				G[i].push_back(make_pair(D_G[i][j].t,D_G[i][j].w));
			}
		}
	}
	int ret=mod*2;
	int L=-1;
	int R=-1;
	for(int i=0;i<a;i++)for(int j=0;j<G[i].size();j++){
		for(int k=0;k<b;k++){
			zu[k]=nxt[k]=-1;
		}
		for(int k=0;k<=a;k++){
			dis[k]=fin[k]=low[k]=par[k]=dep[k]=kodat[k]=koptr[k]=0;
			ptr[k]=-1;
		}
		int cur=z[G[i][j].second];
		zeit=0;
		int sz=0;
		for(int k=0;k<b;k++){
			if(x[k]==y[k])continue;
			if(G[i][j].second==k)continue;
			zu[sz]=y[k];
			nxt[sz]=ptr[x[k]];
			ptr[x[k]]=sz++;
			zu[sz]=x[k];
			nxt[sz]=ptr[y[k]];
			ptr[y[k]]=sz++;
		}
		n=a;
		if(ans==1&&ret>cur){
			for(int k=0;k<a;k++)vis[k]=0;
			queue<int>Q;
			Q.push(s);
			vis[s]=1;
			while(Q.size()){
				int at=Q.front();Q.pop();
				for(int k=ptr[at];~k;k=nxt[k]){
					if(!vis[zu[k]]){
						vis[zu[k]]=1;
						Q.push(zu[k]);
					}
				}
			}
			if(vis[t]==0){
				ret=cur;
				L=G[i][j].second;
				R=-1;
			}
		}
		dfsInfos();
		for(int k=0;k<b;k++){
			if(x[k]==y[k])continue;
			if(G[i][j].second==k)continue;
			if(ret>cur+z[k]&&isFatalEdge(x[k],y[k],s,t)&&(f[x[k]][y[k]]==1||(f[x[k]][y[k]]==2&&((i==x[k]&&G[i][j].first==y[k])||(i==y[k]&&G[i][j].first==x[k]))))){
				ret=cur+z[k];
				L=G[i][j].second;
				R=k;
			}
		}
	}
	if(~R){
		printf("%d\n2\n%d %d\n",ret,L+1,R+1);
	}else printf("%d\n1\n%d\n",ret,L+1);
}

D:
人間が解ける問題には見えない。Mo+ハフマン木の回転、とかではないなら結構面白いのかも。

Codeforces Round #366 (Div. 1)

コンテストのバランスを全く考えていない難問セットなのですが、一つ一つはICPCの大変な問題(しかもサンプルも弱い)のような存在で、ただ実装が辛いだけでした。
ちなみにMARVELはヴェノム派です。

A B C D E Place
00:16 (+1) 00:53 (+3) - - - 33rd

A:
なんて事はないやるだけ問題なのですが、iとnumを1箇所間違えました。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
int p[310000];
int at[310000];
vector<int>v[310000];
int main(){
	int a,b;scanf("%d%d",&a,&b);
	int cnt=0;
	int ind=0;
	int num=0;
	for(int i=0;i<b;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		if(x==1){
			cnt++;
			v[y].push_back(num++);
		}else if(x==2){
			while(at[y]<v[y].size()){
				if(!p[v[y][at[y]]])cnt--;
				p[v[y][at[y]]]=1;
				at[y]++;
			}
		}else{
			while(ind<y){
				if(!p[ind])cnt--;
				p[ind]=1;
				ind++;
			}
		}
		printf("%d\n",cnt);
	}
}

B:
類題はもう何個もあると思います。生きている辺が1本のときの場合分けとかが厄介ですが、サンプルには入っていません。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
int x[5100];
long long dp[2][10100];
int A[5100];
int B[5100];
int C[5100];
int D[5100];

int main(){
	int a,b,c;
	scanf("%d%d%d",&a,&b,&c);
	b--;c--;
	for(int i=0;i<a;i++)scanf("%d",x+i);
	x[a]=mod;
	for(int i=0;i<a;i++)scanf("%d",A+i);
	for(int i=0;i<a;i++)scanf("%d",B+i);
	for(int i=0;i<a;i++)scanf("%d",C+i);
	for(int i=0;i<a;i++)scanf("%d",D+i);
	for(int i=0;i<2;i++)for(int j=0;j<10100;j++)dp[i][j]=inf;
	dp[0][0]=0;
	for(int i=0;i<a;i++){
		int t=i%2;
		for(int j=0;j<10100;j++)dp[!t][j]=inf;
		for(int j=0;j<a*2;j++){
			if(dp[t][j]==inf)continue;
			if(i==b){
				if(j>1||(j==1&&i==a-1))dp[!t][j-1]=min(dp[!t][j-1],dp[t][j]+(long long)(j-1)*(x[i+1]-x[i])+C[i]);
				dp[!t][j+1]=min(dp[!t][j+1],dp[t][j]+(long long)(j+1)*(x[i+1]-x[i])+D[i]);
			}else if(i==c){
				if(j>1||(j==1&&i==a-1))dp[!t][j-1]=min(dp[!t][j-1],dp[t][j]+(long long)(j-1)*(x[i+1]-x[i])+A[i]);
				dp[!t][j+1]=min(dp[!t][j+1],dp[t][j]+(long long)(j+1)*(x[i+1]-x[i])+B[i]);
			}else{
				dp[!t][j+2]=min(dp[!t][j+2],dp[t][j]+(long long)(j+2)*(x[i+1]-x[i])+B[i]+D[i]);
				if(j>1)dp[!t][j]=min(dp[!t][j],dp[t][j]+(long long)j*(x[i+1]-x[i])+min(B[i]+C[i],A[i]+D[i]));
				else if(j==1){
					if(b<i)dp[!t][j]=min(dp[!t][j],dp[t][j]+(long long)j*(x[i+1]-x[i])+A[i]+D[i]);
					else dp[!t][j]=min(dp[!t][j],dp[t][j]+(long long)j*(x[i+1]-x[i])+C[i]+B[i]);
				}
				if(j>2||(j==2&&i==a-1))dp[!t][j-2]=min(dp[!t][j-2],dp[t][j]+(long long)(j-2)*(x[i+1]-x[i])+A[i]+C[i]);
			}
		}
	}
	printf("%I64d\n",dp[a%2][0]);
}

C:
¬とかを無視すれば輪と直線しかないので、一次元にできてあとはありがちなDPをするだけですが、結構実装量が多いです。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
int L[110000];
int R[110000];
vector<int>g[110000];
int ABS(int a){
	return max(a,-a);
}
int num=1;
int ord[110000];
int lm[110000];
int v[110000];
pair<int,int> p[110000];
long long dp[110000][2][2][2];
vector<int>q[110000];
int main(){
	int a,b;scanf("%d%d",&b,&a);
	for(int i=0;i<b;i++){
		int x;scanf("%d",&x);
		if(x==1){
			scanf("%d",L+i);
		}else{
			scanf("%d%d",L+i,R+i);
		//	if(ABS(L[i])<ABS(R[i]))swap(L[i],R[i]);
			if(ABS(L[i])!=ABS(R[i])){
				g[ABS(L[i])].push_back(ABS(R[i]));
				g[ABS(R[i])].push_back(ABS(L[i]));
			}
		}
	}
	for(int i=1;i<=a;i++)v[i]=-1;
	for(int i=1;i<=a;i++)p[i]=make_pair(g[i].size(),i);
	std::sort(p+1,p+a+1);
	for(int i=1;i<=a;i++){
		int at=p[i].second;
		if(~v[at])continue;
		int id=at;
		int tl=num;
		if(p[i].first!=2)tl=-1;

		while(1){
			bool ok=false;
			ord[num]=id;
			v[id]=num++;
			lm[id]=tl;
			for(int j=0;j<g[id].size();j++){
				if(~v[g[id][j]])continue;
				ok=true;
				id=g[id][j];
				break;
			}
			if(!ok)break;
		}
	}
	for(int i=0;i<b;i++){
		q[max(v[ABS(L[i])],v[ABS(R[i])])].push_back(i);
	}
//	for(int i=1;i<=a;i++)printf("%d ",v[i]);
//	printf("\n");
	dp[0][0][0][0]=1;
	for(int i=0;i<a;i++){
		for(int j=0;j<2;j++)for(int k=0;k<2;k++)for(int l=0;l<2;l++){
			if(!dp[i][j][k][l])continue;
			for(int m=0;m<2;m++){
				int tl=l;
				int tk=m;
				int tj=j;
				if(lm[ord[i+1]]==i+1)tj=m;

				for(int t=0;t<q[i+1].size();t++){
					int ql=L[q[i+1][t]];
					int qr=R[q[i+1][t]];
					if(ABS(ql)==ord[i+1]){
						if(qr==0){
							if(ql>0&&m)tl^=1;
							if(ql<0&&!m)tl^=1;
						}else if(ABS(qr)==ord[i+1]){
							if(((ql<0)^m)||((qr<0)^m))tl^=1;
						}else if(ABS(qr)==ord[i]){
							if(((ql<0)^m)||((qr<0)^k))tl^=1;
						}else{
							if(((ql<0)^m)||((qr<0)^j))tl^=1;
						}
					}else{
						if(ABS(ql)==ord[i]){
							if(((qr<0)^m)||((ql<0)^k))tl^=1;
						}else{
							if(((qr<0)^m)||((ql<0)^j))tl^=1;
						}
					}
				}
				dp[i+1][tj][tk][tl]=(dp[i+1][tj][tk][tl]+dp[i][j][k][l])%mod;
			}
		}
	}
	long long ret=0;
	for(int i=0;i<2;i++)for(int j=0;j<2;j++)ret+=dp[a][i][j][1];
	ret%=mod;
	printf("%d\n",(int)ret);
}

AIM Tech Round 3 (Div. 1)

Virtual Participationはオンラインジャッジではありませんが......
他の参加者全員が知っている典型を知らないとどうしようもなくなる好例。しっかりと典型を強くしておくことが求められるセット。AとかBとかかなりいやらしいけど。

A B C D E Place
00:02 00:24 (+3) 01:36 (+5) - - 165th

A:
a以外を探してaが出るまで変える。全部aのときがコーナーケース。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
char in[110000];
int main(){
	scanf("%s",in);
	int n=strlen(in);
	bool ok=false;
	for(int i=0;i<n;i++){
		if(in[i]!='a'){
			ok=true;
			for(int j=i;j<n;j++){
				if(in[j]!='a')in[j]--;
				else break;
			}
			break;
		}
	}
	if(!ok){
		in[n-1]='z';
	}
	printf("%s\n",in);
}

B:
0,1の個数が簡単にわかるのであとは少しずつずらして構成。ずらしに失敗しやすいケースがいくつもある。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
long long a[4];
char out[1010000];
int main(){
	for(int i=0;i<4;i++)scanf("%I64d",a+i);
	long long t=0;
	for(int i=0;i<4;i++)t+=a[i];
	if(t==0){
		printf("1\n");return 0;
	}
	int sz=0;
	while((long long)sz*(sz-1)/2<t)sz++;
	if((long long)sz*(sz-1)/2!=t){
		printf("Impossible\n");return 0;
	}
	for(int i=0;i<=sz;i++){
		long long B=(long long)i*(i-1)/2;
		long long W=(long long)(sz-i)*(sz-i-1)/2;
		if(a[0]==B&&a[3]==W){
			if(a[1]+a[2]!=(long long)i*(sz-i)){
				printf("Impossible\n");return 0;
			}
			if(i==0){
				for(int j=0;j<sz;j++)printf("1");printf("\n");return 0;
			}else if(i==sz){
				for(int j=0;j<sz;j++)printf("0");printf("\n");return 0;
			}

			int ind=0;
			for(int j=0;j<a[2]/i;j++)out[ind++]='1';
			for(int j=0;j<i-a[2]%i;j++)out[ind++]='0';
			out[ind++]='1';
			for(int j=0;j<a[2]%i;j++)out[ind++]='0';
			for(int j=0;j<sz-i-a[2]/i-1;j++)out[ind++]='1';
				out[sz]=0;
			printf("%s\n",out);
			return 0;
		}
	}
	printf("Impossible\n");
}

C:
初めて全方位木DPというものに触れた。O(次数^2)かかると落ちるから名前がついているというのも全然知らなかったので勝手にTLEを出しまくっていた。
感覚的には、根のない木で木DPする感じ?

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
vector<int>g[410000];
pair<int,int> z[820000];
int L[820000];
int R[820000];
int dp[820000][2];
int v[820000];
int num;
void dfs(int a,int b){
	for(int i=0;i<g[a].size();i++){
		L[num]=a;R[num]=g[a][i];
		z[num++]=make_pair(a,g[a][i]);
	}
	for(int i=0;i<g[a].size();i++){
		if(g[a][i]==b)continue;
		dfs(g[a][i],a);
	}
}
int n;
int vis[410000];
int ans[410000];
int un[410000];
int fin(int a,int b){
	return lower_bound(z,z+num,make_pair(a,b))-z;
}
pair<int,int> ef[410000][2];
pair<int,int> tm[410000][2];
pair<int,int> calc(int a,int b){
	int at=lower_bound(z,z+num,make_pair(a,b))-z;
	if(v[at])return make_pair(dp[at][0],dp[at][1]);
	v[at]=1;
	if(!vis[a]){
		vis[a]=1;

		for(int i=0;i<g[a].size();i++){
			if(g[a][i]==b){
				un[a]=i;
				continue;
			}
			pair<int,int> tmp=calc(g[a][i],a);
			dp[at][0]+=tmp.first;
			if(ef[a][0].first<tmp.first-tmp.second){
				ef[a][1]=ef[a][0];ef[a][0]=make_pair(tmp.first-tmp.second,g[a][i]);
			}else if(ef[a][1].first<tmp.first-tmp.second){
				ef[a][1]=make_pair(tmp.first-tmp.second,g[a][i]);
			}
			if(tm[a][0].first<tmp.first){
				tm[a][1]=tm[a][0];tm[a][0]=make_pair(tmp.first,g[a][i]);
			}else if(tm[a][1].first<tmp.first){
				tm[a][1]=make_pair(tmp.first,g[a][i]);
			}
		}
		dp[at][0]++;
	}else{
		if(un[a]!=-1){
			int ind=un[a];
			un[a]=-1;
			pair<int,int> tmp=calc(g[a][ind],a);
			if(ef[a][0].first<tmp.first-tmp.second){
				ef[a][1]=ef[a][0];ef[a][0]=make_pair(tmp.first-tmp.second,g[a][ind]);
			}else if(ef[a][1].first<tmp.first-tmp.second){
				ef[a][1]=make_pair(tmp.first-tmp.second,g[a][ind]);
			}
			if(tm[a][0].first<tmp.first){
				tm[a][1]=tm[a][0];tm[a][0]=make_pair(tmp.first,g[a][ind]);
			}else if(tm[a][1].first<tmp.first){
				tm[a][1]=make_pair(tmp.first,g[a][ind]);
			}
		}
		dp[at][0]=n-dp[fin(b,a)][0];
	}
	int ef1,tm1;
	if(ef[a][0].second!=b)ef1=ef[a][0].first;
	else ef1=ef[a][1].first;
	if(tm[a][0].second!=b)tm1=tm[a][0].first;
	else tm1=tm[a][1].first;
	dp[at][1]=dp[at][0]-ef1;
	if(tm1<=n/2)dp[at][1]=min(dp[at][1],dp[at][0]-tm1);
	
	//printf("%d %d: %d %d\n",a,b,dp[at][0],dp[at][1]);
	return make_pair(dp[at][0],dp[at][1]);
}
int main(){
	int a;scanf("%d",&a);
	n=a;
	for(int i=0;i<a-1;i++){
		int p,q;scanf("%d%d",&p,&q);
		p--;q--;
		g[p].push_back(q);
		g[q].push_back(p);
	}
	dfs(0,-1);
	std::sort(z,z+num);
	for(int i=0;i<num;i++){
		L[i]=z[i].first;R[i]=z[i].second;
	}
	for(int i=0;i<num;i++){
		if(v[i])continue;
		calc(L[i],R[i]);
	}
	for(int i=0;i<a;i++){
		bool ok=true;
		for(int j=0;j<g[i].size();j++){
			int at=lower_bound(z,z+num,make_pair(g[i][j],i))-z;
			if(dp[at][1]>a/2)ok=false;
		}
		if(ok)ans[i]=1;
	}
	for(int i=0;i<a;i++){
		if(i)printf(" ");
		printf("%d",ans[i]);
	}
	printf("\n");
}

D:
線形計画の式にはできたけどこれの双対が良い性質なのかどうかはわからないので、とりあえず線形計画の式だけ書いておくことにする。
そもそも双対の取り方がいまいちよくわからない。将来的に困るからちゃんとこういうの高校で授業して。
[条件]

  • f_{e1}>=0
  • f_{e2}>=0
  • d_{e}>=0
  • sum(in)(f_{e0}+f_{e1}-f_{e2})-sum(out)(f_{e0}+f_{e1}-f_{e2}) = 0
  • c_{e}+d_{e} >= f_{e0}+f_{e1}-f_{e2} >= 0

minimize sum d_{e} + sum f_{e1} + sum f_{e2}

Codeforces Round #371 (Div. 1)

ク ソ コ ン テ ス ト

A B C D E Place
00:04 00:37 00:20 01:36 - 7th

A:
もちろんmultisetなぞ必要なく、偶奇の組み合わせごとに何個あるか持っておけばよい。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
char in[20];
int c[1<<18];
int main(){
	int a;scanf("%d",&a);
	while(a--){
		scanf("%s",in);
		long long b;
		scanf("%I64d",&b);
		if(in[0]=='+'){
			int now=0;
			for(int i=0;i<18;i++){
				if(b%2)now+=(1<<i);
				b/=10;
			}
			c[now]++;
		}else if(in[0]=='-'){
			int now=0;
			for(int i=0;i<18;i++){
				if(b%2)now+=(1<<i);
				b/=10;
			}
			c[now]--;
		}else{
			int now=0;
			for(int i=0;i<18;i++){
				if(b%2)now+=(1<<i);
				b/=10;
			}
			printf("%d\n",c[now]);
		}
	}
}

B:
二分探索でどこに上下左右の辺があるかがわかる。あとはどの辺がどの長方形に対応しているのかを全部試せばよい。結構実装がトリッキー。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
int chk(int a,int b,int c,int d){
	if(a>c||b>d)return 0;
	printf("? %d %d %d %d\n",a,b,c,d);
	fflush(stdout);
	int ret;scanf("%d",&ret);
	return ret;
}
int val[4][2];
int main(){
	int a;scanf("%d",&a);
	for(int i=0;i<4;i++){
		for(int j=0;j<2;j++){
			int left=0;
			int right=a+1;
			while(left+1<right){
				int M=(left+right)/2;
				int T,B,L,R;
				if(i==0){
					T=1;B=M;L=1;R=a;
				}else if(i==1){
					T=M;B=a;L=1;R=a;
				}else if(i==2){
					T=1;B=a;L=1;R=M;
				}else{
					T=1;B=a;L=M;R=a;
				}
				int val=chk(T,L,B,R);
				if(val>j){
					if(i%2==0){
						right=M;
					}else{
						left=M;
					}
				}else{
					if(i%2==0){
						left=M;
					}else right=M;
				}
			}
			if(i%2==0){
				val[i][j]=right;
			}else val[i][j]=left;
		}
	}
	for(int i=0;i<8;i++){
		if(chk(val[1][0],val[3][i/4],val[0][i%4/2],val[2][i%2])&&chk(val[1][1],val[3][!(i/4)],val[0][!(i%4/2)],val[2][!(i%2)])){
			printf("! %d %d %d %d %d %d %d %d\n",val[1][0],val[3][i/4],val[0][i%4/2],val[2][i%2],val[1][1],val[3][!(i/4)],val[0][!(i%4/2)],val[2][!(i%2)]);fflush(stdout);
			return 0;
		}
	}
}

C:
何度同じ問題が出れば気が済むんだ...

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
int b[3100];
long long dp[3100][3100];
int z[3100];
long long ABS(long long a){
	return max(a,-a);
}
int main(){
	int a;scanf("%d",&a);
	for(int i=0;i<a;i++)scanf("%d",b+i);
	for(int i=0;i<a;i++)b[i]-=i;
	for(int i=0;i<a;i++)z[i]=b[i];
		std::sort(z,z+a);
	for(int i=0;i<3100;i++)for(int j=0;j<3100;j++)dp[i][j]=inf;
	dp[0][0]=0;
	for(int i=1;i<=a;i++){
		long long tmp=inf;
		for(int j=0;j<a;j++){
			tmp=min(tmp,dp[i-1][j]);
			dp[i][j]=tmp+ABS(b[i-1]-z[j]);
		}
	}
	long long ret=inf;
	for(int i=0;i<a;i++)ret=min(ret,dp[a][i]);
	printf("%I64d\n",ret);
}

D:
そういや最大長方形忘れてたなと思ってググった。
クエリの中で二分探索をすると、答えがk以上かどうか判断するのは、長方形内の値の最大値がわかれば容易。
計算量が中途半端にきつく、そのままsegtreeとかにしてしまうと落ちる。1Dでも2Dでもいいのでsparse tableにしておけば、オーダーがわずかに落ちて間に合う。(いかにも強引に通してしまった別解のような解法だが、実際解説にはこれが書かれています......)

スパソを貼ったらポインタの代入がわからないので適当に配列に書き換えた。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;

int segtree[2048][11000];
void buildRMQ(int *a, int n,int to) {
  int logn = 1;
  for (int k = 1; k < n; k *= 2) ++logn;
  int *r = new int[n * logn];
  int *b = r; copy(a, a+n, b);
  for (int k = 1; k < n; k *= 2) {
    copy(b, b+n, b+n); b += n;
    for(int i=0;i< n-k;i++) b[i] = min(b[i], b[i+k]);
  }
for(int j=0;j<n*logn;j++)segtree[to][j]=r[j];
}
int minimum(int x, int y, int *rmq, int n) {
  int z = y - x, k = 0, e = 1, s; // y - x >= e = 2^k なる最大 k
  s = ( (z & 0xffff0000) != 0 ) << 4; z >>= s; e <<= s; k |= s;
  s = ( (z & 0x0000ff00) != 0 ) << 3; z >>= s; e <<= s; k |= s;
  s = ( (z & 0x000000f0) != 0 ) << 2; z >>= s; e <<= s; k |= s;
  s = ( (z & 0x0000000c) != 0 ) << 1; z >>= s; e <<= s; k |= s;
  s = ( (z & 0x00000002) != 0 ) << 0; z >>= s; e <<= s; k |= s;
  return min( rmq[x+n*k], rmq[y+n*k-e+1] );
}
int dp[1010][1010];
int f[2048][1100];
int c[1010][1010];
int L,R;
int m;
int query(int a,int b,int c,int d,int e){
	if(d<a||b<c)return 0;
	if(c<=a&&b<=d){
	//	printf("%d %d %d %d\n",a,b,L,R);fflush(stdout);
		int ret=minimum(L,R,segtree[e],m);
		return ret;
	}
	return min(query(a,(a+b)/2,c,d,e*2),query((a+b)/2+1,b,c,d,e*2+1));
}
int main(){
	int a,b;
	scanf("%d%d",&a,&b);
	m=b;
	for(int i=0;i<a;i++){
		for(int j=0;j<b;j++)scanf("%d",&c[i][j]);
	}
	for(int i=0;i<a;i++)for(int j=0;j<b;j++){
		if(!c[i][j])continue;
		int tmp=mod;
		if(i==0||j==0)tmp=0;
		else tmp=min(min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1]);
		dp[i][j]=tmp+1;
	}

	for(int i=0;i<a;i++)for(int j=0;j<b;j++)
		f[i+1024][j]=-dp[i][j];
	for(int i=2047;i>0;i--){
		for(int j=0;j<b;j++){
			f[i/2][j]=min(f[i/2][j],f[i][j]);
		}
		buildRMQ(f[i],b,i);
	//	segtree[i]=buildRMQ(f[i],b);
	}
	int T;scanf("%d",&T);
	while(T--){
		int p,q,r,s;
		scanf("%d%d%d%d",&p,&q,&r,&s);
		p--;q--;r--;s--;
		int left=0;
		int right=min(r-p+1,s-q+1)+1;
		while(left+1<right){
			int M=(left+right)/2;
			int t1=p+M-1;
			int t2=q+M-1;
			R=s;L=t2;
			//printf("%d %d\n",l,r);
			if(query(0,1023,t1,r,1)<=-M){
				left=M;
			}else{
				right=M;
			}
		}
		printf("%d\n",left);
	}
}

Codeforces Round #372 (Div. 1)

単純に2時間コンテストとは思えないほど難しい。

A B C D E Place
00:08 00:44 - - - 56th

A:
(i+1)^2(i+2)^2にしてからsqrtを使う。(i+1)^2(i+2)^2はlong longに入らないが、あらかじめ(i+1)で割っておけば対処可能。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
long long pv[110000];
long long ret[110000];
int main(){
	int a;scanf("%d",&a);
	pv[0]=2;
	for(int i=1;i<a;i++){
		pv[i]=(long long)i*(i+1);
	}
	for(int i=0;i<a;i++){
		long long t=(long long)(i+1)*(i+2);
		long long s=t*(i+2);
		ret[i]=s-pv[i]/(i+1);
	}
	for(int i=0;i<a;i++)printf("%I64d\n",ret[i]);
}

B:
ふだんCに置いてあるような難度のB。逆からコストの決まった辺だけで最短距離を求めておいて、L未満にならないような最小のコストをつけてたどっていく。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
vector<pair<int,int> > g[1100];
long long ijk[2][1100];
int v[2][1100];
int main(){
	int a,b,c,d,e;
	scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);
	for(int i=0;i<b;i++){
		int p,q,r;scanf("%d%d%d",&p,&q,&r);
		g[p].push_back(make_pair(q,r));
		g[q].push_back(make_pair(p,r));
	}
	for(int i=0;i<2;i++)for(int j=0;j<a;j++)ijk[i][j]=inf;
	priority_queue<pair<long long,int> > Q;
	Q.push(make_pair(0,d));
	ijk[0][d]=0;
	while(Q.size()){
		long long cost=-Q.top().first;
		int at=Q.top().second;Q.pop();
		if(v[0][at])continue;
		v[0][at]=1;
		for(int i=0;i<g[at].size();i++){
			int to=g[at][i].first;
			if(g[at][i].second==0)continue;
			long long toc=cost+g[at][i].second;
			if(v[0][to]||ijk[0][to]<=toc)continue;
			ijk[0][to]=toc;
			Q.push(make_pair(-toc,to));
		}
	}
	if(ijk[0][e]<c){
		printf("NO\n");return 0;
	}
	Q.push(make_pair(0,e));
	ijk[1][e]=0;
	map<pair<int,int>,long long>M;
	while(Q.size()){
		long long cost=-Q.top().first;
		int at=Q.top().second;Q.pop();
		if(v[1][at])continue;
		v[1][at]=1;
		for(int i=0;i<g[at].size();i++){
			int to=g[at][i].first;
			long long toc=cost+g[at][i].second;
			if(g[at][i].second==0){
				if(M.count(make_pair(min(at,to),max(at,to))))toc=cost+M[make_pair(min(at,to),max(at,to))];
				else{
					if(cost+1+ijk[0][to]>=c){
						toc=cost+1;
						M[make_pair(min(at,to),max(at,to))]=1;
					}else{
						toc=(c-ijk[0][to]);
						M[make_pair(min(at,to),max(at,to))]=c-cost-ijk[0][to];
					}
				}
			}
			if(v[1][to]||ijk[1][to]<=toc)continue;
			ijk[1][to]=toc;
			Q.push(make_pair(-toc,to));
		}
	}
	if(ijk[1][d]>c){
		printf("NO\n");return 0;
	}
	printf("YES\n");
	for(int i=0;i<a;i++)for(int j=0;j<g[i].size();j++){
		if(i>g[i][j].first)continue;
		if(g[i][j].second)printf("%d %d %d\n",i,g[i][j].first,g[i][j].second);
		else printf("%d %d %I64d\n",i,g[i][j].first,M[make_pair(i,g[i][j].first)]);
	}
}

C:
あからさまな重心分解に加え重心分解の中身を線形並みの速度にするのも難しそうなのでスルーした。また今度。

D:

田^^^8888_______________
 田^^^8888____________|
  田^^^8888__________|
   田^^^8888________|
    田^^^8888______|

みたいなのをつなげる。それぞれの田で6倍に増えて、隣の8をうまく使うと0~5倍にできるので、6進数で表記するのに対応する。
6^24>10^18なのでぎりぎり収まる。ただし右下の8を置くスペースだけは足りなくなるので、強引に押し込む。

こういう問題はそもそもサンプルがあってるのかすらわからない上デバッグしようにもどこから手をつけたらいいのかわからないので、大変困ります。良い解決方法はあるのでしょうか。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
int y[60][60];
int t[60][60];
int main(){
	long long a;
	scanf("%I64d",&a);
	for(int i=0;i<23;i++){
		t[i*2+2][i*2]=t[i*2+2][i*2+1]=1;
		y[i*2+1][i*2+2]=1;
		int f=a%6;
	//	printf("%d",f);
		y[i*2][i*2+2+f]=1;
	//	if(i==22)break;
		for(int j=0;j<7;j++){
			if(i*2+3+j<49)t[i*2+1][i*2+3+j]=1;
		}
		if(i==22){
			if(f==4){
				y[i*2][i*2+2+2]=1;
				t[i*2+1][48]=0;
			}else if(f==5){
				t[i*2+1][48]=0;
			}
		}
		y[i*2+1][i*2+10]=1;
		y[i*2+2][i*2+10]=1;
		a/=6;
	}
	//printf("%d\n",(int)a);
	if(!a)y[46][46]=1;
	//printf("\n");
	printf("47 50\n");
	vector<pair<pair<int,int>,pair<int,int> > >ans;
	for(int i=0;i<60;i++)for(int j=0;j<60;j++){
		if(i+1<47&&j<50&&t[i][j])ans.push_back(make_pair(make_pair(i+1,j+1),make_pair(i+2,j+1)));
		if(i<47&&j+1<50&&y[i][j])ans.push_back(make_pair(make_pair(i+1,j+1),make_pair(i+1,j+2)));
		
	}
	printf("%d\n",(int)(ans.size()));
	for(int i=0;i<ans.size();i++){
		printf("%d %d %d %d\n",ans[i].first.first,ans[i].first.second,ans[i].second.first,ans[i].second.second);
	}
}

Codeforces Round #373 (Div. 1)

紫コーダーはDiv1のwriterやるのやめてほしい。

A B C D E Place
00:13 削除*1 01:02 -4 - 42th

A:
一番左に出てきた5を四捨五入していく。ひどい実装問題。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
char in[210000];
char str[210000];
int main(){
	int a,b;scanf("%d%d",&a,&b);
	scanf("%s",in);
	int ind=0;
	int sz=0;
	for(int i=0;i<a;i++){
		if(in[i]=='.')ind=i;
		else str[sz++]=in[i];
	}
	int one=0;
	for(int i=0;i<sz;i++){

		if(i>=ind&&str[i]>='5'&&str[i]<='9'){
			int at=i;
			while(b--){
				if(at<ind)break;
				if(str[at]>='5'&&str[at]<='9'){
					if(at==0){
						one++;
					}else{
						str[at-1]++;
						int t=at-1;
						while(str[t]>'9'){
							str[t]-=10;
							if(t==0){
								one=1;break;
							}
							else str[t-1]++;
							t--;
						}
					}
				}else break;
				sz=at;
				at--;
			}
			break;
		}
	}
	
	while(sz>ind){
		if(str[sz-1]=='0')sz--;
		else break;
	}
	if(one)printf("1");
	for(int i=0;i<sz;i++){
		if(i==ind)printf(".");
		printf("%c",str[i]);
		
	}printf("\n");
}

B:
(^_^;)

C:
例の区間加算と区間和のsegtreeで行列やベクトルで値を持つ。こんな実装をする前に行列やベクトルのライブラリを用意すべきである。それにしても全く面白くない問題。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
struct mat{
	long long t[2][2];
	mat(){t[0][0]=t[0][1]=t[1][0]=t[1][1]=0;}
};

mat seg1[262144];
bool flag[262144];
long long seg2[262144][2];
int p[110000];
mat tmp;
mat I;
mat F(int a){
	mat ret;
	mat val;
	ret.t[0][0]=ret.t[1][1]=1;
	val.t[0][0]=val.t[0][1]=val.t[1][0]=1;
	while(a){
		if(a%2){
			for(int i=0;i<2;i++)for(int j=0;j<2;j++)tmp.t[i][j]=0;
			for(int k=0;k<2;k++)for(int i=0;i<2;i++)for(int j=0;j<2;j++)tmp.t[i][j]=(tmp.t[i][j]+ret.t[i][k]*val.t[k][j])%mod;
			for(int i=0;i<2;i++)for(int j=0;j<2;j++)ret.t[i][j]=tmp.t[i][j];
		}
		a/=2;
		for(int i=0;i<2;i++)for(int j=0;j<2;j++)tmp.t[i][j]=0;
		for(int k=0;k<2;k++)for(int i=0;i<2;i++)for(int j=0;j<2;j++)tmp.t[i][j]=(tmp.t[i][j]+val.t[i][k]*val.t[k][j])%mod;
		for(int i=0;i<2;i++)for(int j=0;j<2;j++)val.t[i][j]=tmp.t[i][j];
	}
	return ret;
}
long long t2[2];
void add(int a,int b,int c,int d,int e,mat f){
	if(d<a||b<c)return;
	if(c<=a&&b<=d){
		flag[e]=true;
		t2[0]=seg2[e][0]*f.t[0][0]+seg2[e][1]*f.t[0][1];
		t2[1]=seg2[e][0]*f.t[1][0]+seg2[e][1]*f.t[1][1];
		seg2[e][0]=t2[0]%mod;
		seg2[e][1]=t2[1]%mod;
		for(int i=0;i<2;i++)for(int j=0;j<2;j++)tmp.t[i][j]=0;
		for(int k=0;k<2;k++)for(int i=0;i<2;i++)for(int j=0;j<2;j++)tmp.t[i][j]=(tmp.t[i][j]+f.t[i][k]*seg1[e].t[k][j])%mod;
		for(int i=0;i<2;i++)for(int j=0;j<2;j++)seg1[e].t[i][j]=tmp.t[i][j];
		return;
	}
	if(flag[e]){
		flag[e*2]=flag[e*2+1]=true;
		for(int i=0;i<2;i++)for(int j=0;j<2;j++)tmp.t[i][j]=0;
		for(int k=0;k<2;k++)for(int i=0;i<2;i++)for(int j=0;j<2;j++)tmp.t[i][j]=(tmp.t[i][j]+seg1[e].t[i][k]*seg1[e*2].t[k][j])%mod;
		for(int i=0;i<2;i++)for(int j=0;j<2;j++)seg1[e*2].t[i][j]=tmp.t[i][j];
		t2[0]=seg2[e*2][0]*seg1[e].t[0][0]+seg2[e*2][1]*seg1[e].t[0][1];
		t2[1]=seg2[e*2][0]*seg1[e].t[1][0]+seg2[e*2][1]*seg1[e].t[1][1];
		seg2[e*2][0]=t2[0]%mod;
		seg2[e*2][1]=t2[1]%mod;
		for(int i=0;i<2;i++)for(int j=0;j<2;j++)tmp.t[i][j]=0;
		for(int k=0;k<2;k++)for(int i=0;i<2;i++)for(int j=0;j<2;j++)tmp.t[i][j]=(tmp.t[i][j]+seg1[e].t[i][k]*seg1[e*2+1].t[k][j])%mod;
		for(int i=0;i<2;i++)for(int j=0;j<2;j++)seg1[e*2+1].t[i][j]=tmp.t[i][j];
		t2[0]=seg2[e*2+1][0]*seg1[e].t[0][0]+seg2[e*2+1][1]*seg1[e].t[0][1];
		t2[1]=seg2[e*2+1][0]*seg1[e].t[1][0]+seg2[e*2+1][1]*seg1[e].t[1][1];
		seg2[e*2+1][0]=t2[0]%mod;
		seg2[e*2+1][1]=t2[1]%mod;
		flag[e]=false;
		seg1[e]=I;
	}
	add(a,(a+b)/2,c,d,e*2,f);
	add((a+b)/2+1,b,c,d,e*2+1,f);
	seg2[e][0]=(seg2[e*2][0]+seg2[e*2+1][0])%mod;
	seg2[e][1]=(seg2[e*2][1]+seg2[e*2+1][1])%mod;
	
}
long long calc(int a,int b,int c,int d,int e){
	if(d<a||b<c)return 0;
	if(c<=a&&b<=d){
	//	return (seg2[e][0]*seg1[e].t[0][0]+seg2[e][1]*seg1[e].t[0][1])%mod;
		return seg2[e][0];
	}
	if(flag[e]){
		flag[e*2]=flag[e*2+1]=true;
		for(int i=0;i<2;i++)for(int j=0;j<2;j++)tmp.t[i][j]=0;
		for(int k=0;k<2;k++)for(int i=0;i<2;i++)for(int j=0;j<2;j++)tmp.t[i][j]=(tmp.t[i][j]+seg1[e].t[i][k]*seg1[e*2].t[k][j])%mod;
		for(int i=0;i<2;i++)for(int j=0;j<2;j++)seg1[e*2].t[i][j]=tmp.t[i][j];
		t2[0]=seg2[e*2][0]*seg1[e].t[0][0]+seg2[e*2][1]*seg1[e].t[0][1];
		t2[1]=seg2[e*2][0]*seg1[e].t[1][0]+seg2[e*2][1]*seg1[e].t[1][1];
		seg2[e*2][0]=t2[0]%mod;
		seg2[e*2][1]=t2[1]%mod;
		for(int i=0;i<2;i++)for(int j=0;j<2;j++)tmp.t[i][j]=0;
		for(int k=0;k<2;k++)for(int i=0;i<2;i++)for(int j=0;j<2;j++)tmp.t[i][j]=(tmp.t[i][j]+seg1[e].t[i][k]*seg1[e*2+1].t[k][j])%mod;
		for(int i=0;i<2;i++)for(int j=0;j<2;j++)seg1[e*2+1].t[i][j]=tmp.t[i][j];
		t2[0]=seg2[e*2+1][0]*seg1[e].t[0][0]+seg2[e*2+1][1]*seg1[e].t[0][1];
		t2[1]=seg2[e*2+1][0]*seg1[e].t[1][0]+seg2[e*2+1][1]*seg1[e].t[1][1];
		seg2[e*2+1][0]=t2[0]%mod;
		seg2[e*2+1][1]=t2[1]%mod;
		flag[e]=false;
		seg1[e]=I;
	}
	long long ret=(calc(a,(a+b)/2,c,d,e*2)+calc((a+b)/2+1,b,c,d,e*2+1))%mod;

	seg2[e][0]=(seg2[e*2][0]+seg2[e*2+1][0])%mod;
	seg2[e][1]=(seg2[e*2][1]+seg2[e*2+1][1])%mod;
	return ret;
}
int main(){
	int a,b;scanf("%d%d",&a,&b);
	for(int i=0;i<a;i++){
		scanf("%d",p+i);
	}
	for(int i=0;i<a;i++){
		mat tmp=F(p[i]-1);
		seg2[i+131072][0]=tmp.t[0][0];
		seg2[i+131072][1]=tmp.t[1][0];

	}
	for(int i=131071;i>=0;i--){
		seg2[i][0]=(seg2[i*2][0]+seg2[i*2+1][0])%mod;
		seg2[i][1]=(seg2[i*2][1]+seg2[i*2+1][1])%mod;
	}
	for(int i=0;i<262144;i++){
		for(int j=0;j<2;j++)for(int k=0;k<2;k++)seg1[i].t[j][k]=(j==k);
	}
	I.t[0][0]=I.t[1][1]=1;
	while(b--){
		int v;scanf("%d",&v);
		if(v==1){
			int x,y,z;scanf("%d%d%d",&x,&y,&z);x--;y--;
			add(0,131071,x,y,1,F(z));
		}else{
			int x,y;scanf("%d%d",&x,&y);x--;y--;
			printf("%I64d\n",calc(0,131071,x,y,1));
		}
	}
}

D:
重心を求めてから対称性をチェックしつつ非対称な場所を答えに加えるみたいなことをしたけどWAなので、嘘解法なのだろうか。確かにAC人数は少ないけども。

UPD: 解法は合っていた。木の同型性判定をするときは普通のローリングハッシュだと衝突が狙って簡単に起こせるらしく、頂点に変な係数をかけてやったりしないといけないらしい。そのうえジャッジデータには mod 2^k ローリングハッシュを落とすケースもちゃんと存在していた。
今回解いた中で唯一解きがいがあった。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
#include<set>
#include<stdlib.h>
using namespace std;
const long long mod=1000000007;
const long long inf=mod*mod;
typedef unsigned long long wolf;
vector<int>g[110000];
wolf key[110000];
int st[110000];
int ra[110000];
wolf ran[]={114514,1919810,364364,4545334};
int n;
const long long M=1000000009;
int dfs(int a,int b){
	int rem=n-1;
	for(int i=0;i<g[a].size();i++){
		if(g[a][i]==b)continue;
		int tmp=dfs(g[a][i],a);
		ra[a]=max(ra[a],tmp);
		rem-=tmp;
		st[a]+=tmp;
	}
	ra[a]=max(ra[a],rem);
	st[a]++;
	return st[a];
}
vector<int>gp;
wolf calc(int a,int b){
	vector<wolf> ch;
	for(int i=0;i<g[a].size();i++){
		if(g[a][i]==b)continue;
		ch.push_back(calc(g[a][i],a));
	}
	std::sort(ch.begin(),ch.end());
	wolf ret=ch.size()+1;
	for(int i=0;i<ch.size();i++){
		ret=(ret*mod+ch[i]*ran[i])%M;
	}
	key[a]=ret;
	return ret;
}
int ans;
void cnt(int a,int b){
	if(g[a].size()<4){
		ans++;
	}
	vector<pair<wolf,int> > v;
	for(int i=0;i<g[a].size();i++){
		if(g[a][i]==b)continue;
		v.push_back(make_pair(key[g[a][i]],g[a][i]));
	}
	std::sort(v.begin(),v.end());
	for(int i=0;i<v.size();i++){
		if(i==0||v[i].first!=v[i-1].first){
			cnt(v[i].second,a);
		}
	}
}
int main(){
	int a;scanf("%d",&a);
	for(int i=0;i<a-1;i++){
		int p,q;scanf("%d%d",&p,&q);p--;q--;
		g[p].push_back(q);g[q].push_back(p);
	}
	n=a;
	dfs(0,-1);
	int mv=999999999;
	//for(int i=0;i<a;i++)printf("%d\n",ra[i]);
	for(int i=0;i<a;i++)mv=min(mv,ra[i]);
	for(int i=0;i<a;i++)if(ra[i]==mv)gp.push_back(i);
	
	if(gp.size()>2){
		printf("yabai\n");while(1);
	}else if(gp.size()==2){
		calc(gp[0],gp[1]);
		calc(gp[1],gp[0]);
		cnt(gp[0],gp[1]);
		if(key[gp[0]]!=key[gp[1]])cnt(gp[1],gp[0]);
	}else{
		calc(gp[0],-1);
		cnt(gp[0],-1);
	}
	printf("%d\n",ans);
}

*1:多分出題ミスで消えたんだと思う。跡形もなく消え去っていた。